feat(lib/git): annotate REST handlers with utoipa and add GitApiDoc
ZhenYi committed
Modified lib/git/src/handler/analysis/code_activity.rs lib/git/src/handler/analysis/code_activity.rs +10 −1 View lib/git/src/handler/analysis/code_activity.rs
@@ -7,12 +7,21 @@
77use crate::service::GitService;
88use crate::service::analysis::CodeActivityDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct CodeActivityQuery {
1212 #[serde(default)]
1313 pub branch: Option<String>,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/analysis/code-activity",
19 tag = "analysis",
20 params(crate::handler::RepoPath, CodeActivityQuery),
21 responses(
22 (status = 200, description = "code activity analysis", body = AppResponseData<CodeActivityDto>)
23 )
24)]
1625pub async fn code_activity(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/analysis/contributors.rs lib/git/src/handler/analysis/contributors.rs +9 −0 View lib/git/src/handler/analysis/contributors.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::analysis::ContributorStatDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/analysis/contributors",
13 tag = "analysis",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "contributor statistics", body = AppResponseData<Vec<ContributorStatDto>>)
17 )
18)]
1019pub async fn contributors(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/analysis/languages.rs lib/git/src/handler/analysis/languages.rs +9 −0 View lib/git/src/handler/analysis/languages.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::analysis::LanguageStatDto;
99
10#[utoipa::path(
11 post,
12 path = "/repos/{space_name}/{repo_name}/analysis/languages",
13 tag = "analysis",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "analyze languages", body = AppResponseData<Vec<LanguageStatDto>>)
17 )
18)]
1019pub async fn languages(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/analysis/languages_read.rs lib/git/src/handler/analysis/languages_read.rs +9 −0 View lib/git/src/handler/analysis/languages_read.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::analysis::LanguageStatDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/analysis/languages",
13 tag = "analysis",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "cached language analysis", body = AppResponseData<Vec<LanguageStatDto>>)
17 )
18)]
1019pub async fn languages_read(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/archives/create.rs lib/git/src/handler/archives/create.rs +10 −0 View lib/git/src/handler/archives/create.rs
@@ -8,6 +8,16 @@
88use crate::service::GitService;
99use crate::service::archives::{ArchiveOptionsDto, ArchiveResultDto};
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/archives/create/{revision}",
14 tag = "archives",
15 params(RepoPathRev),
16 request_body = String,
17 responses(
18 (status = 200, description = "create archive", body = AppResponseData<ArchiveResultDto>)
19 )
20)]
1121pub async fn create(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/archives/create_bundle.rs lib/git/src/handler/archives/create_bundle.rs +9 −0 View lib/git/src/handler/archives/create_bundle.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::archives::ArchiveResultDto;
99
10#[utoipa::path(
11 post,
12 path = "/repos/{space_name}/{repo_name}/archives/bundle",
13 tag = "archives",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "create git bundle", body = AppResponseData<ArchiveResultDto>)
17 )
18)]
1019pub async fn create_bundle(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/archives/download.rs lib/git/src/handler/archives/download.rs +13 −0 View lib/git/src/handler/archives/download.rs
@@ -7,6 +7,19 @@
77use crate::service::GitService;
88use crate::service::archives::{ArchiveOptionsDto, ArchiveResultDto};
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/archives/download/{revision}",
13 tag = "archives",
14 params(
15 RepoPathRev,
16 ("format" = String, Query, description = "archive format"),
17 ("path_filter" = Option<String>, Query, description = "path filter"),
18 ),
19 responses(
20 (status = 200, description = "download archive", body = AppResponseData<ArchiveResultDto>)
21 )
22)]
1023pub async fn download(
1124 State(service): State<GitService>,
1225 session: Session,
Modified lib/git/src/handler/blobs/content.rs lib/git/src/handler/blobs/content.rs +10 −1 View lib/git/src/handler/blobs/content.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::blobs::BlobContentDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BlobPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub oid: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/blobs/{oid}/content",
20 tag = "blobs",
21 params(BlobPath),
22 responses(
23 (status = 200, description = "blob content", body = AppResponseData<BlobContentDto>)
24 )
25)]
1726pub async fn blob_content(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/blobs/list_lfs_pointers.rs lib/git/src/handler/blobs/list_lfs_pointers.rs +14 −2 View lib/git/src/handler/blobs/list_lfs_pointers.rs
@@ -7,19 +7,31 @@
77use crate::service::GitService;
88use crate::service::blobs::LfsPointerDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct RepoQuery {
1212 pub space_name: String,
1313 pub repo_name: String,
1414}
1515
16#[derive(Debug, Clone, serde::Deserialize)]
16#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1717pub struct LfsPointerQuery {
1818 pub revision: String,
1919 #[serde(default)]
2020 pub limit: Option<u32>,
2121}
2222
23#[utoipa::path(
24 get,
25 path = "/repos/{space_name}/{repo_name}/lfs-pointers",
26 tag = "blobs",
27 params(
28 RepoQuery,
29 LfsPointerQuery,
30 ),
31 responses(
32 (status = 200, description = "list lfs pointers", body = AppResponseData<Vec<LfsPointerDto>>)
33 )
34)]
2335pub async fn list_lfs_pointers(
2436 State(service): State<GitService>,
2537 session: Session,
Modified lib/git/src/handler/blobs/metadata.rs lib/git/src/handler/blobs/metadata.rs +10 −1 View lib/git/src/handler/blobs/metadata.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::blobs::BlobMetaDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BlobPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub oid: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/blobs/{oid}/metadata",
20 tag = "blobs",
21 params(BlobPath),
22 responses(
23 (status = 200, description = "blob metadata", body = AppResponseData<BlobMetaDto>)
24 )
25)]
1726pub async fn blob_metadata(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/blobs/raw_download.rs lib/git/src/handler/blobs/raw_download.rs +10 −1 View lib/git/src/handler/blobs/raw_download.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::blobs::RawDownloadDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BlobPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub oid: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/blobs/{oid}/raw",
20 tag = "blobs",
21 params(BlobPath),
22 responses(
23 (status = 200, description = "blob raw download info", body = AppResponseData<RawDownloadDto>)
24 )
25)]
1726pub async fn blob_raw_download(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/branches/create.rs lib/git/src/handler/branches/create.rs +10 −0 View lib/git/src/handler/branches/create.rs
@@ -8,6 +8,16 @@
88use crate::service::branches::{BranchDto, CreateBranchCmd};
99use crate::service::GitService;
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/branches",
14 tag = "branches",
15 params(crate::handler::RepoPath),
16 request_body = CreateBranchCmd,
17 responses(
18 (status = 200, description = "create branch", body = AppResponseData<BranchDto>)
19 )
20)]
1121pub async fn create_branch(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/branches/delete.rs lib/git/src/handler/branches/delete.rs +11 −2 View lib/git/src/handler/branches/delete.rs
@@ -6,19 +6,28 @@
66use crate::handler::require_writer;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct BranchPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub name: String,
1414}
1515
16#[derive(Debug, Clone, serde::Deserialize)]
16#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1717pub struct DeleteBranchQuery {
1818 #[serde(default)]
1919 pub force: Option<bool>,
2020}
2121
22#[utoipa::path(
23 delete,
24 path = "/repos/{space_name}/{repo_name}/branches/{name}",
25 tag = "branches",
26 params(BranchPath, DeleteBranchQuery),
27 responses(
28 (status = 200, description = "delete branch", body = AppResponseData<NoData>)
29 )
30)]
2231pub async fn delete_branch(
2332 State(service): State<GitService>,
2433 session: Session,
Modified lib/git/src/handler/branches/get.rs lib/git/src/handler/branches/get.rs +10 −1 View lib/git/src/handler/branches/get.rs
@@ -7,13 +7,22 @@
77use crate::service::branches::BranchDto;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BranchPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub name: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/branches/{name}",
20 tag = "branches",
21 params(BranchPath),
22 responses(
23 (status = 200, description = "get branch", body = AppResponseData<BranchDto>)
24 )
25)]
1726pub async fn get_branch(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/branches/list.rs lib/git/src/handler/branches/list.rs +9 −0 View lib/git/src/handler/branches/list.rs
@@ -7,6 +7,15 @@
77use crate::service::branches::BranchDto;
88use crate::service::GitService;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/branches",
13 tag = "branches",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "list branches", body = AppResponseData<Vec<BranchDto>>)
17 )
18)]
1019pub async fn list_branches(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/branches/rename.rs lib/git/src/handler/branches/rename.rs +11 −2 View lib/git/src/handler/branches/rename.rs
@@ -6,18 +6,27 @@
66use crate::handler::require_writer;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct RenameBranchPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub from: String,
1414}
1515
16#[derive(Debug, Clone, serde::Deserialize)]
16#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1717pub struct RenameBranchQuery {
1818 pub to: String,
1919}
2020
21#[utoipa::path(
22 patch,
23 path = "/repos/{space_name}/{repo_name}/branches/{from}",
24 tag = "branches",
25 params(RenameBranchPath, RenameBranchQuery),
26 responses(
27 (status = 200, description = "rename branch", body = AppResponseData<NoData>)
28 )
29)]
2130pub async fn rename_branch(
2231 State(service): State<GitService>,
2332 session: Session,
Modified lib/git/src/handler/branches/set_default.rs lib/git/src/handler/branches/set_default.rs +10 −1 View lib/git/src/handler/branches/set_default.rs
@@ -6,13 +6,22 @@
66use crate::handler::require_writer;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct BranchPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub name: String,
1414}
1515
16#[utoipa::path(
17 post,
18 path = "/repos/{space_name}/{repo_name}/branches/{name}/default",
19 tag = "branches",
20 params(BranchPath),
21 responses(
22 (status = 200, description = "set default branch", body = AppResponseData<NoData>)
23 )
24)]
1625pub async fn set_default_branch(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/commits/get.rs lib/git/src/handler/commits/get.rs +10 −1 View lib/git/src/handler/commits/get.rs
@@ -7,13 +7,22 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct CommitPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub revision: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/commits/{revision}",
20 tag = "commits",
21 params(CommitPath),
22 responses(
23 (status = 200, description = "get commit")
24 )
25)]
1726pub async fn get_commit(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/commits/is_ancestor.rs lib/git/src/handler/commits/is_ancestor.rs +11 −2 View lib/git/src/handler/commits/is_ancestor.rs
@@ -7,17 +7,26 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct AncestorQuery {
1212 pub ancestor: String,
1313 pub descendant: String,
1414}
1515
16#[derive(Debug, Clone, Serialize)]
16#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1717pub struct IsAncestorResponse {
1818 pub is_ancestor: bool,
1919}
2020
21#[utoipa::path(
22 get,
23 path = "/repos/{space_name}/{repo_name}/commits/ancestor",
24 tag = "commits",
25 params(crate::handler::RepoPath, AncestorQuery),
26 responses(
27 (status = 200, description = "check commit ancestry", body = AppResponseData<IsAncestorResponse>)
28 )
29)]
2130pub async fn is_ancestor(
2231 State(service): State<GitService>,
2332 session: Session,
Modified lib/git/src/handler/commits/list.rs lib/git/src/handler/commits/list.rs +17 −0 View lib/git/src/handler/commits/list.rs
@@ -8,6 +8,23 @@
88use crate::service::GitService;
99use crate::service::commits::ListCommitsCmd;
1010
11#[utoipa::path(
12 get,
13 path = "/repos/{space_name}/{repo_name}/commits",
14 tag = "commits",
15 params(
16 crate::handler::RepoPath,
17 ("revision" = Option<String>, Query, description = "start revision"),
18 ("path" = Option<String>, Query, description = "path filter"),
19 ("max_count" = Option<u32>, Query, description = "max commits"),
20 ("since" = Option<String>, Query, description = "since date"),
21 ("until" = Option<String>, Query, description = "until date"),
22 ("first_parent" = bool, Query, description = "first parent only"),
23 ),
24 responses(
25 (status = 200, description = "list commits")
26 )
27)]
1128pub async fn list_commits(
1229 State(service): State<GitService>,
1330 session: Session,
Modified lib/git/src/handler/commits/list_commit_statuses.rs lib/git/src/handler/commits/list_commit_statuses.rs +10 −1 View lib/git/src/handler/commits/list_commit_statuses.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::commits::CommitStatusDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct StatusPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub sha: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/commit-statuses/{sha}",
20 tag = "commits",
21 params(StatusPath),
22 responses(
23 (status = 200, description = "list commit statuses", body = AppResponseData<Vec<CommitStatusDto>>)
24 )
25)]
1726pub async fn list_commit_statuses(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/commits/rev_parse.rs lib/git/src/handler/commits/rev_parse.rs +10 −1 View lib/git/src/handler/commits/rev_parse.rs
@@ -6,13 +6,22 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct RevPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub revision: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/rev/{revision}",
19 tag = "commits",
20 params(RevPath),
21 responses(
22 (status = 200, description = "parse revision", body = AppResponseData<String>)
23 )
24)]
1625pub async fn rev_parse(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/commits/set_commit_status.rs lib/git/src/handler/commits/set_commit_status.rs +10 −0 View lib/git/src/handler/commits/set_commit_status.rs
@@ -8,6 +8,16 @@
88use crate::service::GitService;
99use crate::service::commits::{CommitStatusDto, SetCommitStatusCmd};
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/commit-statuses",
14 tag = "commits",
15 params(crate::handler::RepoPath),
16 request_body = SetCommitStatusCmd,
17 responses(
18 (status = 200, description = "set commit status", body = AppResponseData<CommitStatusDto>)
19 )
20)]
1121pub async fn set_commit_status(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/commits/stats.rs lib/git/src/handler/commits/stats.rs +10 −1 View lib/git/src/handler/commits/stats.rs
@@ -9,13 +9,22 @@
99use crate::handler::resolve_reader;
1010use crate::service::GitService;
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1313pub struct StatsQuery {
1414 pub revision: String,
1515 #[serde(default)]
1616 pub path: Option<String>,
1717}
1818
19#[utoipa::path(
20 get,
21 path = "/repos/{space_name}/{repo_name}/commits/stats",
22 tag = "commits",
23 params(crate::handler::RepoPath, StatsQuery),
24 responses(
25 (status = 200, description = "commit stats")
26 )
27)]
1928pub async fn commit_stats(
2029 State(service): State<GitService>,
2130 session: Session,
Modified lib/git/src/handler/commits/sync_commit.rs lib/git/src/handler/commits/sync_commit.rs +11 −2 View lib/git/src/handler/commits/sync_commit.rs
@@ -7,19 +7,28 @@
77use crate::handler::require_writer;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct CommitPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub revision: String,
1515}
1616
17#[derive(Debug, Clone, Serialize)]
17#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1818pub struct CommitSyncResponse {
1919 pub repo_id: String,
2020 pub sha: String,
2121}
2222
23#[utoipa::path(
24 post,
25 path = "/repos/{space_name}/{repo_name}/commits/{revision}/sync",
26 tag = "commits",
27 params(CommitPath),
28 responses(
29 (status = 200, description = "sync commit", body = AppResponseData<CommitSyncResponse>)
30 )
31)]
2332pub async fn sync_commit(
2433 State(service): State<GitService>,
2534 session: Session,
Modified lib/git/src/handler/commits/tree_entries.rs lib/git/src/handler/commits/tree_entries.rs +11 −2 View lib/git/src/handler/commits/tree_entries.rs
@@ -9,19 +9,28 @@
99use crate::handler::resolve_reader;
1010use crate::service::GitService;
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1313pub struct TreePath {
1414 pub space_name: String,
1515 pub repo_name: String,
1616 pub revision: String,
1717}
1818
19#[derive(Debug, Clone, Default, serde::Deserialize)]
19#[derive(Debug, Clone, Default, serde::Deserialize, utoipa::IntoParams)]
2020pub struct TreeQuery {
2121 #[serde(default)]
2222 pub path: Option<String>,
2323}
2424
25#[utoipa::path(
26 get,
27 path = "/repos/{space_name}/{repo_name}/commits/{revision}/tree",
28 tag = "commits",
29 params(TreePath, TreeQuery),
30 responses(
31 (status = 200, description = "commit tree entries")
32 )
33)]
2534pub async fn get_tree_entries(
2635 State(service): State<GitService>,
2736 session: Session,
Modified lib/git/src/handler/conflicts/check.rs lib/git/src/handler/conflicts/check.rs +10 −1 View lib/git/src/handler/conflicts/check.rs
@@ -7,12 +7,21 @@
77use crate::service::GitService;
88use crate::service::conflicts::MergeStatusDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct ConflictBaseHeadQuery {
1212 pub base: String,
1313 pub head: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/conflicts/check",
19 tag = "conflicts",
20 params(crate::handler::RepoPath, ConflictBaseHeadQuery),
21 responses(
22 (status = 200, description = "check merge conflicts", body = AppResponseData<MergeStatusDto>)
23 )
24)]
1625pub async fn conflict_check(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/conflicts/check_threeway.rs lib/git/src/handler/conflicts/check_threeway.rs +11 −1 View lib/git/src/handler/conflicts/check_threeway.rs
@@ -8,13 +8,23 @@
88use crate::service::GitService;
99use crate::service::conflicts::MergeStatusDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct ConflictCheckThreewayBody {
1313 pub ours: String,
1414 pub theirs: String,
1515 pub ancestor: String,
1616}
1717
18#[utoipa::path(
19 post,
20 path = "/repos/{space_name}/{repo_name}/conflicts/check-threeway",
21 tag = "conflicts",
22 params(crate::handler::RepoPath),
23 request_body = ConflictCheckThreewayBody,
24 responses(
25 (status = 200, description = "three-way merge conflict check", body = AppResponseData<MergeStatusDto>)
26 )
27)]
1828pub async fn conflict_check_threeway(
1929 State(service): State<GitService>,
2030 session: Session,
Modified lib/git/src/handler/conflicts/conflicted_files.rs lib/git/src/handler/conflicts/conflicted_files.rs +10 −1 View lib/git/src/handler/conflicts/conflicted_files.rs
@@ -6,12 +6,21 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct ConflictBaseHeadQuery {
1111 pub base: String,
1212 pub head: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/conflicts/files",
18 tag = "conflicts",
19 params(crate::handler::RepoPath, ConflictBaseHeadQuery),
20 responses(
21 (status = 200, description = "conflicted files", body = AppResponseData<Vec<String>>)
22 )
23)]
1524pub async fn conflicted_files(
1625 State(service): State<GitService>,
1726 session: Session,
Modified lib/git/src/handler/conflicts/detail.rs lib/git/src/handler/conflicts/detail.rs +10 −1 View lib/git/src/handler/conflicts/detail.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::conflicts::ConflictDetailDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct ConflictDetailQuery {
1212 pub base: String,
1313 pub head: String,
1414 pub path: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/conflicts/detail",
20 tag = "conflicts",
21 params(crate::handler::RepoPath, ConflictDetailQuery),
22 responses(
23 (status = 200, description = "conflict detail", body = AppResponseData<ConflictDetailDto>)
24 )
25)]
1726pub async fn conflict_detail(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/content/caller_signature.rs lib/git/src/handler/content/caller_signature.rs +10 −1 View lib/git/src/handler/content/caller_signature.rs
@@ -9,12 +9,21 @@
99use crate::service::GitService;
1010use crate::service::content::SignatureDto;
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1313pub struct CallerSignatureBody {
1414 #[serde(default)]
1515 pub when: Option<String>,
1616}
1717
18#[utoipa::path(
19 post,
20 path = "/repos/_/contents/signature",
21 tag = "content",
22 request_body = CallerSignatureBody,
23 responses(
24 (status = 200, description = "caller git signature", body = AppResponseData<SignatureDto>)
25 )
26)]
1827pub async fn caller_signature(
1928 State(service): State<GitService>,
2029 session: Session,
Modified lib/git/src/handler/content/commit_object.rs lib/git/src/handler/content/commit_object.rs +11 −1 View lib/git/src/handler/content/commit_object.rs
@@ -8,7 +8,7 @@
88use crate::service::GitService;
99use crate::service::content::{ObjectKind, SignatureDto, WriteObjectResultDto};
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct CommitObjectBody {
1313 pub kind: ObjectKind,
1414 pub parents: Vec<String>,
@@ -18,6 +18,16 @@
1818 pub committer: SignatureDto,
1919}
2020
21#[utoipa::path(
22 post,
23 path = "/repos/{space_name}/{repo_name}/contents/commits",
24 tag = "content",
25 params(RepoPath),
26 request_body = CommitObjectBody,
27 responses(
28 (status = 200, description = "write commit object", body = AppResponseData<WriteObjectResultDto>)
29 )
30)]
2131pub async fn commit_object(
2232 State(service): State<GitService>,
2333 session: Session,
Modified lib/git/src/handler/content/write_blob.rs lib/git/src/handler/content/write_blob.rs +11 −1 View lib/git/src/handler/content/write_blob.rs
@@ -8,11 +8,21 @@
88use crate::service::GitService;
99use crate::service::content::WriteObjectResultDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct WriteBlobBody {
1313 pub data: String,
1414}
1515
16#[utoipa::path(
17 post,
18 path = "/repos/{space_name}/{repo_name}/contents/blobs",
19 tag = "content",
20 params(RepoPath),
21 request_body = WriteBlobBody,
22 responses(
23 (status = 200, description = "write blob object", body = AppResponseData<WriteObjectResultDto>)
24 )
25)]
1626pub async fn write_blob(
1727 State(service): State<GitService>,
1828 session: Session,
Modified lib/git/src/handler/content/write_tree_from_files.rs lib/git/src/handler/content/write_tree_from_files.rs +11 −1 View lib/git/src/handler/content/write_tree_from_files.rs
@@ -8,11 +8,21 @@
88use crate::service::GitService;
99use crate::service::content::{FileEntryDto, WriteObjectResultDto};
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct WriteTreeBody {
1313 pub files: Vec<FileEntryDto>,
1414}
1515
16#[utoipa::path(
17 post,
18 path = "/repos/{space_name}/{repo_name}/contents/trees",
19 tag = "content",
20 params(RepoPath),
21 request_body = WriteTreeBody,
22 responses(
23 (status = 200, description = "write tree from files", body = AppResponseData<WriteObjectResultDto>)
24 )
25)]
1626pub async fn write_tree_from_files(
1727 State(service): State<GitService>,
1828 session: Session,
Modified lib/git/src/handler/diffs/files.rs lib/git/src/handler/diffs/files.rs +13 −1 View lib/git/src/handler/diffs/files.rs
@@ -8,12 +8,24 @@
88use crate::service::GitService;
99use crate::service::diffs::DiffOptionsDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct DiffBaseHeadQuery {
1313 pub base: String,
1414 pub head: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/diff/files-with-context",
20 tag = "diffs",
21 params(
22 crate::handler::RepoPath,
23 DiffBaseHeadQuery,
24 ),
25 responses(
26 (status = 200, description = "changed files with context")
27 )
28)]
1729pub async fn diff_files(
1830 State(service): State<GitService>,
1931 session: Session,
Modified lib/git/src/handler/diffs/files_changed_between.rs lib/git/src/handler/diffs/files_changed_between.rs +13 −1 View lib/git/src/handler/diffs/files_changed_between.rs
@@ -6,12 +6,24 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct DiffBaseHeadQuery {
1111 pub base: String,
1212 pub head: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/diff/files",
18 tag = "diffs",
19 params(
20 crate::handler::RepoPath,
21 DiffBaseHeadQuery,
22 ),
23 responses(
24 (status = 200, description = "files changed between revisions", body = AppResponseData<Vec<String>>)
25 )
26)]
1527pub async fn files_changed_between(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/diffs/for_commit.rs lib/git/src/handler/diffs/for_commit.rs +10 −1 View lib/git/src/handler/diffs/for_commit.rs
@@ -7,13 +7,22 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct CommitPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub commit: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/diff/for-commit/{commit}",
20 tag = "diffs",
21 params(CommitPath),
22 responses(
23 (status = 200, description = "changed paths for commit")
24 )
25)]
1726pub async fn diff_for_commit(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/diffs/range_diff.rs lib/git/src/handler/diffs/range_diff.rs +10 −0 View lib/git/src/handler/diffs/range_diff.rs
@@ -8,6 +8,16 @@
88use crate::handler::resolve_reader;
99use crate::service::GitService;
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/diff/range",
14 tag = "diffs",
15 params(crate::handler::RepoPath),
16 request_body = String,
17 responses(
18 (status = 200, description = "range diff", body = AppResponseData<String>)
19 )
20)]
1121pub async fn range_diff(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/diffs/raw.rs lib/git/src/handler/diffs/raw.rs +13 −1 View lib/git/src/handler/diffs/raw.rs
@@ -6,12 +6,24 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct DiffBaseHeadQuery {
1111 pub base: String,
1212 pub head: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/diff/raw",
18 tag = "diffs",
19 params(
20 crate::handler::RepoPath,
21 DiffBaseHeadQuery,
22 ),
23 responses(
24 (status = 200, description = "raw diff", body = AppResponseData<String>)
25 )
26)]
1527pub async fn diff_raw(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/diffs/short_stat.rs lib/git/src/handler/diffs/short_stat.rs +13 −1 View lib/git/src/handler/diffs/short_stat.rs
@@ -7,12 +7,24 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct DiffBaseHeadQuery {
1212 pub base: String,
1313 pub head: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/diff/shortstat",
19 tag = "diffs",
20 params(
21 crate::handler::RepoPath,
22 DiffBaseHeadQuery,
23 ),
24 responses(
25 (status = 200, description = "diff short stat")
26 )
27)]
1628pub async fn diff_short_stat(
1729 State(service): State<GitService>,
1830 session: Session,
Modified lib/git/src/handler/diffs/stats.rs lib/git/src/handler/diffs/stats.rs +13 −1 View lib/git/src/handler/diffs/stats.rs
@@ -8,12 +8,24 @@
88use crate::service::GitService;
99use crate::service::diffs::DiffOptionsDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct DiffBaseHeadQuery {
1313 pub base: String,
1414 pub head: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/diff/stats",
20 tag = "diffs",
21 params(
22 crate::handler::RepoPath,
23 DiffBaseHeadQuery,
24 ),
25 responses(
26 (status = 200, description = "diff stats")
27 )
28)]
1729pub async fn diff_stats(
1830 State(service): State<GitService>,
1931 session: Session,
Modified lib/git/src/handler/diffs/tree.rs lib/git/src/handler/diffs/tree.rs +13 −1 View lib/git/src/handler/diffs/tree.rs
@@ -7,7 +7,7 @@
77use crate::service::GitService;
88use crate::service::diffs::DiffTreeRecordDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct DiffTreeQuery {
1212 pub base: String,
1313 pub head: String,
@@ -15,6 +15,18 @@
1515 pub use_merge_base: Option<bool>,
1616}
1717
18#[utoipa::path(
19 get,
20 path = "/repos/{space_name}/{repo_name}/diff/tree",
21 tag = "diffs",
22 params(
23 crate::handler::RepoPath,
24 DiffTreeQuery,
25 ),
26 responses(
27 (status = 200, description = "diff trees", body = AppResponseData<Vec<DiffTreeRecordDto>>)
28 )
29)]
1830pub async fn diff_tree(
1931 State(service): State<GitService>,
2032 session: Session,
Modified lib/git/src/handler/hooks/execute_script.rs lib/git/src/handler/hooks/execute_script.rs +12 −2 View lib/git/src/handler/hooks/execute_script.rs
@@ -8,19 +8,29 @@
88use crate::service::GitService;
99use crate::service::hooks::ScriptOutputDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct HookScriptPath {
1313 pub space_name: String,
1414 pub repo_name: String,
1515 pub script: String,
1616}
1717
18#[derive(Debug, Clone, serde::Deserialize)]
18#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1919pub struct HookExecBody {
2020 #[serde(default)]
2121 pub args: Vec<String>,
2222}
2323
24#[utoipa::path(
25 post,
26 path = "/repos/{space_name}/{repo_name}/hooks/{script}",
27 tag = "hooks",
28 params(HookScriptPath),
29 request_body = HookExecBody,
30 responses(
31 (status = 200, description = "execute hook script", body = AppResponseData<ScriptOutputDto>)
32 )
33)]
2434pub async fn execute_script(
2535 State(service): State<GitService>,
2636 session: Session,
Modified lib/git/src/handler/lfs/cleanup_unreferenced.rs lib/git/src/handler/lfs/cleanup_unreferenced.rs +10 −1 View lib/git/src/handler/lfs/cleanup_unreferenced.rs
@@ -6,11 +6,20 @@
66use crate::handler::{RepoPath, require_writer};
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Serialize)]
9#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
1010pub struct LfsCleanupResponse {
1111 pub removed: i64,
1212}
1313
14#[utoipa::path(
15 post,
16 path = "/repos/{space_name}/{repo_name}/lfs/cleanup",
17 tag = "lfs",
18 params(RepoPath),
19 responses(
20 (status = 200, description = "cleanup unreferenced lfs objects", body = AppResponseData<LfsCleanupResponse>)
21 )
22)]
1423pub async fn cleanup_unreferenced(
1524 State(service): State<GitService>,
1625 session: Session,
Modified lib/git/src/handler/lfs/list_locks.rs lib/git/src/handler/lfs/list_locks.rs +10 −1 View lib/git/src/handler/lfs/list_locks.rs
@@ -7,7 +7,7 @@
77use crate::service::GitService;
88use crate::service::lfs::LfsLockListResponse;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct LfsListLocksQuery {
1212 #[serde(default)]
1313 pub cursor: Option<String>,
@@ -15,6 +15,15 @@
1515 pub limit: Option<u64>,
1616}
1717
18#[utoipa::path(
19 get,
20 path = "/repos/{space_name}/{repo_name}/lfs/locks",
21 tag = "lfs",
22 params(RepoPath, LfsListLocksQuery),
23 responses(
24 (status = 200, description = "list lfs locks", body = AppResponseData<LfsLockListResponse>)
25 )
26)]
1827pub async fn list_locks(
1928 State(service): State<GitService>,
2029 session: Session,
Modified lib/git/src/handler/lfs/lock.rs lib/git/src/handler/lfs/lock.rs +11 −1 View lib/git/src/handler/lfs/lock.rs
@@ -8,13 +8,23 @@
88use crate::service::GitService;
99use crate::service::lfs::LfsLock;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct LfsLockBody {
1313 pub path: String,
1414 #[serde(default)]
1515 pub oid: Option<String>,
1616}
1717
18#[utoipa::path(
19 post,
20 path = "/repos/{space_name}/{repo_name}/lfs/locks",
21 tag = "lfs",
22 params(RepoPath),
23 request_body = LfsLockBody,
24 responses(
25 (status = 200, description = "lock lfs path", body = AppResponseData<LfsLock>)
26 )
27)]
1828pub async fn lock(
1929 State(service): State<GitService>,
2030 session: Session,
Modified lib/git/src/handler/lfs/unlock.rs lib/git/src/handler/lfs/unlock.rs +29 −4 View lib/git/src/handler/lfs/unlock.rs
@@ -4,25 +4,50 @@
44use session::Session;
55
66use crate::errors::{ApiError, GitApiError};
7use crate::handler::{RepoPath, require_writer};
7use crate::handler::{RepoPath, require_repo_admin, require_writer};
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1111pub struct LfsUnlockBody {
1212 pub path: String,
1313 #[serde(default)]
1414 pub force: Option<bool>,
1515}
1616
17#[utoipa::path(
18 delete,
19 path = "/repos/{space_name}/{repo_name}/lfs/locks",
20 tag = "lfs",
21 params(RepoPath),
22 request_body = LfsUnlockBody,
23 responses(
24 (status = 200, description = "unlock lfs path", body = AppResponseData<NoData>)
25 )
26)]
1727pub async fn unlock(
1828 State(service): State<GitService>,
1929 session: Session,
2030 Path(path): Path<RepoPath>,
2131 Json(body): Json<LfsUnlockBody>,
2232) -> Result<AppResponseData<NoData>, ApiError> {
23 let (_ctx, uid) = require_writer(&service, &session, path.space_name.clone(), path.repo_name.clone()).await?;
33 let force = body.force.unwrap_or(false);
34 let uid = if force {
35 require_repo_admin(
36 &service,
37 &session,
38 path.space_name.clone(),
39 path.repo_name.clone(),
40 )
41 .await?
42 .1
43 } else {
44 let (_ctx, uid) =
45 require_writer(&service, &session, path.space_name.clone(), path.repo_name.clone())
46 .await?;
47 uid
48 };
2449 service
25 .lfs_unlock(path.space_name, path.repo_name, uid, body.path, body.force.unwrap_or(false))
50 .lfs_unlock(path.space_name, path.repo_name, uid, body.path, force)
2651 .await
2752 .map_err(GitApiError::from)?;
2853 Ok(AppResponseData::ok(NoData))
Modified lib/git/src/handler/lfs/unlock_by_id.rs lib/git/src/handler/lfs/unlock_by_id.rs +30 −5 View lib/git/src/handler/lfs/unlock_by_id.rs
@@ -4,36 +4,61 @@
44use session::Session;
55
66use crate::errors::{ApiError, GitApiError};
7use crate::handler::require_writer;
7use crate::handler::{require_repo_admin, require_writer};
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct LfsLockIdPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub lock_id: String,
1515}
1616
17#[derive(Debug, Clone, serde::Deserialize)]
17#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1818pub struct LfsUnlockByIdBody {
1919 #[serde(default)]
2020 pub force: Option<bool>,
2121}
2222
23#[utoipa::path(
24 delete,
25 path = "/repos/{space_name}/{repo_name}/lfs/locks/{lock_id}",
26 tag = "lfs",
27 params(LfsLockIdPath),
28 request_body = LfsUnlockByIdBody,
29 responses(
30 (status = 200, description = "unlock lfs lock by id", body = AppResponseData<NoData>)
31 )
32)]
2333pub async fn unlock_by_id(
2434 State(service): State<GitService>,
2535 session: Session,
2636 Path(path): Path<LfsLockIdPath>,
2737 Json(body): Json<LfsUnlockByIdBody>,
2838) -> Result<AppResponseData<NoData>, ApiError> {
29 let (_ctx, uid) = require_writer(&service, &session, path.space_name.clone(), path.repo_name.clone()).await?;
39 let force = body.force.unwrap_or(false);
40 let uid = if force {
41 require_repo_admin(
42 &service,
43 &session,
44 path.space_name.clone(),
45 path.repo_name.clone(),
46 )
47 .await?
48 .1
49 } else {
50 let (_ctx, uid) =
51 require_writer(&service, &session, path.space_name.clone(), path.repo_name.clone())
52 .await?;
53 uid
54 };
3055 service
3156 .lfs_unlock_by_id(
3257 path.space_name,
3358 path.repo_name,
3459 uid,
3560 path.lock_id,
36 body.force.unwrap_or(false),
61 force,
3762 )
3863 .await
3964 .map_err(GitApiError::from)?;
Modified lib/git/src/handler/lfs/verify_locks.rs lib/git/src/handler/lfs/verify_locks.rs +10 −1 View lib/git/src/handler/lfs/verify_locks.rs
@@ -7,12 +7,21 @@
77use crate::service::GitService;
88use crate::service::lfs::LfsVerifyLocksResponse;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct LfsVerifyLocksQuery {
1212 #[serde(default)]
1313 pub limit: Option<u64>,
1414}
1515
16#[utoipa::path(
17 post,
18 path = "/repos/{space_name}/{repo_name}/lfs/locks/verify",
19 tag = "lfs",
20 params(RepoPath, LfsVerifyLocksQuery),
21 responses(
22 (status = 200, description = "verify lfs locks", body = AppResponseData<LfsVerifyLocksResponse>)
23 )
24)]
1625pub async fn verify_locks(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/logs/commits_between.rs lib/git/src/handler/logs/commits_between.rs +13 −1 View lib/git/src/handler/logs/commits_between.rs
@@ -7,7 +7,7 @@
77use crate::service::GitService;
88use crate::service::logs::LogListDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BetweenQuery {
1212 pub after: String,
1313 pub before: String,
@@ -15,6 +15,18 @@
1515 pub limit: Option<u64>,
1616}
1717
18#[utoipa::path(
19 get,
20 path = "/repos/{space_name}/{repo_name}/logs/between",
21 tag = "logs",
22 params(
23 crate::handler::RepoPath,
24 BetweenQuery,
25 ),
26 responses(
27 (status = 200, description = "commits between two revisions")
28 )
29)]
1830pub async fn log_commits_between(
1931 State(service): State<GitService>,
2032 session: Session,
Modified lib/git/src/handler/logs/graph.rs lib/git/src/handler/logs/graph.rs +12 −0 View lib/git/src/handler/logs/graph.rs
@@ -7,6 +7,18 @@
77use crate::service::GitService;
88use crate::service::logs::{GraphNode, LogOptions};
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/logs/graph",
13 tag = "logs",
14 params(
15 crate::handler::RepoPath,
16 LogOptions,
17 ),
18 responses(
19 (status = 200, description = "commit graph")
20 )
21)]
1022pub async fn log_graph(
1123 State(service): State<GitService>,
1224 session: Session,
Modified lib/git/src/handler/logs/last_commit.rs lib/git/src/handler/logs/last_commit.rs +13 −1 View lib/git/src/handler/logs/last_commit.rs
@@ -7,11 +7,23 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct LastCommitQuery {
1212 pub branch: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/logs/last",
18 tag = "logs",
19 params(
20 crate::handler::RepoPath,
21 LastCommitQuery,
22 ),
23 responses(
24 (status = 200, description = "last commit on branch")
25 )
26)]
1527pub async fn log_last_commit(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/logs/list.rs lib/git/src/handler/logs/list.rs +12 −0 View lib/git/src/handler/logs/list.rs
@@ -7,6 +7,18 @@
77use crate::service::GitService;
88use crate::service::logs::{LogListDto, LogOptions};
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/logs",
13 tag = "logs",
14 params(
15 crate::handler::RepoPath,
16 LogOptions,
17 ),
18 responses(
19 (status = 200, description = "list commits")
20 )
21)]
1022pub async fn log_list(
1123 State(service): State<GitService>,
1224 session: Session,
Modified lib/git/src/handler/maintenance/fsck.rs lib/git/src/handler/maintenance/fsck.rs +9 −0 View lib/git/src/handler/maintenance/fsck.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::maintenance::FsckDto;
99
10#[utoipa::path(
11 post,
12 path = "/repos/{space_name}/{repo_name}/maintenance/fsck",
13 tag = "maintenance",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "repository fsck", body = AppResponseData<FsckDto>)
17 )
18)]
1019pub async fn fsck(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/maintenance/prune.rs lib/git/src/handler/maintenance/prune.rs +9 −0 View lib/git/src/handler/maintenance/prune.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::maintenance::PruneResultDto;
99
10#[utoipa::path(
11 post,
12 path = "/repos/{space_name}/{repo_name}/maintenance/prune",
13 tag = "maintenance",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "repository prune", body = AppResponseData<PruneResultDto>)
17 )
18)]
1019pub async fn prune(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/packs/ingest.rs lib/git/src/handler/packs/ingest.rs +10 −0 View lib/git/src/handler/packs/ingest.rs
@@ -8,6 +8,16 @@
88use crate::service::GitService;
99use crate::service::packs::IngestPackDto;
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/packs/ingest",
14 tag = "packs",
15 params(RepoPath),
16 request_body = String,
17 responses(
18 (status = 200, description = "ingest pack file", body = AppResponseData<IngestPackDto>)
19 )
20)]
1121pub async fn ingest(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/packs/repack.rs lib/git/src/handler/packs/repack.rs +9 −0 View lib/git/src/handler/packs/repack.rs
@@ -6,6 +6,15 @@
66use crate::handler::{RepoPath, require_writer};
77use crate::service::GitService;
88
9#[utoipa::path(
10 post,
11 path = "/repos/{space_name}/{repo_name}/packs/repack",
12 tag = "packs",
13 params(RepoPath),
14 responses(
15 (status = 200, description = "repack repository", body = AppResponseData<NoData>)
16 )
17)]
918pub async fn repack(
1019 State(service): State<GitService>,
1120 session: Session,
Modified lib/git/src/handler/packs/stats.rs lib/git/src/handler/packs/stats.rs +9 −0 View lib/git/src/handler/packs/stats.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::packs::PackStatsDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/packs/stats",
13 tag = "packs",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "pack statistics", body = AppResponseData<PackStatsDto>)
17 )
18)]
1019pub async fn stats(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/patches/apply.rs lib/git/src/handler/patches/apply.rs +11 −1 View lib/git/src/handler/patches/apply.rs
@@ -8,12 +8,22 @@
88use crate::service::GitService;
99use crate::service::patches::AppliedPatchDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1212pub struct PatchApplyBody {
1313 pub patch: String,
1414 pub base: String,
1515}
1616
17#[utoipa::path(
18 post,
19 path = "/repos/{space_name}/{repo_name}/patch/apply",
20 tag = "patches",
21 params(crate::handler::RepoPath),
22 request_body = PatchApplyBody,
23 responses(
24 (status = 200, description = "apply patch", body = AppResponseData<AppliedPatchDto>)
25 )
26)]
1727pub async fn patch_apply(
1828 State(service): State<GitService>,
1929 session: Session,
Modified lib/git/src/handler/patches/generate.rs lib/git/src/handler/patches/generate.rs +10 −1 View lib/git/src/handler/patches/generate.rs
@@ -7,12 +7,21 @@
77use crate::service::GitService;
88use crate::service::patches::PatchDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct PatchGenerateQuery {
1212 pub base: String,
1313 pub head: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/patch/generate",
19 tag = "patches",
20 params(crate::handler::RepoPath, PatchGenerateQuery),
21 responses(
22 (status = 200, description = "generate patch", body = AppResponseData<PatchDto>)
23 )
24)]
1625pub async fn patch_generate(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/patches/parse.rs lib/git/src/handler/patches/parse.rs +12 −1 View lib/git/src/handler/patches/parse.rs
@@ -4,14 +4,25 @@
44use session::Session;
55
66use crate::errors::{ApiError, GitApiError};
7use crate::handler::ensure_caller;
78use crate::service::GitService;
89use crate::service::patches::ParsedPatchDto;
910
11#[utoipa::path(
12 post,
13 path = "/repos/_/patch/parse",
14 tag = "patches",
15 request_body = String,
16 responses(
17 (status = 200, description = "parse patch", body = AppResponseData<ParsedPatchDto>)
18 )
19)]
1020pub async fn patch_parse(
1121 State(service): State<GitService>,
12 _session: Session,
22 session: Session,
1323 Json(content): Json<String>,
1424) -> Result<AppResponseData<ParsedPatchDto>, ApiError> {
25 ensure_caller(&session)?;
1526 let out = service
1627 .patch_parse(content)
1728 .await
Modified lib/git/src/handler/pipelines/cancel.rs lib/git/src/handler/pipelines/cancel.rs +14 −1 View lib/git/src/handler/pipelines/cancel.rs
@@ -1,19 +1,32 @@
11use axum::Json;
22use axum::extract::State;
33use export::rest::{AppResponseData, NoData};
4use session::Session;
45
56use crate::errors::{ApiError, GitApiError};
7use crate::handler::ensure_caller;
68use crate::service::GitService;
79
8#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
911pub struct PipelineCancelBody {
1012 pub job_id: String,
1113}
1214
15#[utoipa::path(
16 post,
17 path = "/repos/_/pipelines/cancel",
18 tag = "pipelines",
19 request_body = PipelineCancelBody,
20 responses(
21 (status = 200, description = "cancel pipeline job", body = AppResponseData<NoData>)
22 )
23)]
1324pub async fn cancel(
1425 State(service): State<GitService>,
26 session: Session,
1527 Json(body): Json<PipelineCancelBody>,
1628) -> Result<AppResponseData<NoData>, ApiError> {
29 ensure_caller(&session)?;
1730 service
1831 .pipeline_cancel(body.job_id)
1932 .await
Modified lib/git/src/handler/pipelines/schedule.rs lib/git/src/handler/pipelines/schedule.rs +12 −2 View lib/git/src/handler/pipelines/schedule.rs
@@ -8,16 +8,26 @@
88use crate::service::GitService;
99use crate::service::pipelines::JobDto;
1010
11#[derive(Debug, Clone, serde::Deserialize)]
11#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct PipelineScheduleQuery {
1313 pub commit: String,
1414}
1515
16#[derive(Debug, Clone, serde::Deserialize)]
16#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
1717pub struct PipelineScheduleBody {
1818 pub job: JobDto,
1919}
2020
21#[utoipa::path(
22 post,
23 path = "/repos/{space_name}/{repo_name}/pipelines/schedule",
24 tag = "pipelines",
25 params(RepoPath, PipelineScheduleQuery),
26 request_body = PipelineScheduleBody,
27 responses(
28 (status = 200, description = "schedule pipeline job", body = AppResponseData<NoData>)
29 )
30)]
2131pub async fn schedule(
2232 State(service): State<GitService>,
2333 session: Session,
Modified lib/git/src/handler/pipelines/trigger.rs lib/git/src/handler/pipelines/trigger.rs +10 −1 View lib/git/src/handler/pipelines/trigger.rs
@@ -7,12 +7,21 @@
77use crate::service::GitService;
88use crate::service::pipelines::TriggerResultDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct PipelineTriggerQuery {
1212 pub commit: String,
1313 pub branch: String,
1414}
1515
16#[utoipa::path(
17 post,
18 path = "/repos/{space_name}/{repo_name}/pipelines/trigger",
19 tag = "pipelines",
20 params(RepoPath, PipelineTriggerQuery),
21 responses(
22 (status = 200, description = "trigger pipeline", body = AppResponseData<TriggerResultDto>)
23 )
24)]
1625pub async fn trigger(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/refs/branches.rs lib/git/src/handler/refs/branches.rs +10 −1 View lib/git/src/handler/refs/branches.rs
@@ -7,7 +7,7 @@
77use crate::service::GitService;
88use crate::service::refs::BranchInfoDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct RefsBranchesQuery {
1212 #[serde(default)]
1313 pub skip: Option<u64>,
@@ -15,6 +15,15 @@
1515 pub limit: Option<u64>,
1616}
1717
18#[utoipa::path(
19 get,
20 path = "/repos/{space_name}/{repo_name}/refs/branches",
21 tag = "refs",
22 params(crate::handler::RepoPath, RefsBranchesQuery),
23 responses(
24 (status = 200, description = "list branch refs", body = AppResponseData<Vec<BranchInfoDto>>)
25 )
26)]
1827pub async fn refs_branches(
1928 State(service): State<GitService>,
2029 session: Session,
Modified lib/git/src/handler/refs/delete.rs lib/git/src/handler/refs/delete.rs +10 −1 View lib/git/src/handler/refs/delete.rs
@@ -6,13 +6,22 @@
66use crate::handler::require_writer;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct RefPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub name: String,
1414}
1515
16#[utoipa::path(
17 delete,
18 path = "/repos/{space_name}/{repo_name}/refs/{name}",
19 tag = "refs",
20 params(RefPath),
21 responses(
22 (status = 200, description = "delete ref", body = AppResponseData<NoData>)
23 )
24)]
1625pub async fn refs_delete(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/refs/filtered.rs lib/git/src/handler/refs/filtered.rs +10 −1 View lib/git/src/handler/refs/filtered.rs
@@ -7,11 +7,20 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct RefsFilteredQuery {
1212 pub pattern: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/refs/filtered",
18 tag = "refs",
19 params(crate::handler::RepoPath, RefsFilteredQuery),
20 responses(
21 (status = 200, description = "list refs by pattern")
22 )
23)]
1524pub async fn refs_list_filtered(
1625 State(service): State<GitService>,
1726 session: Session,
Modified lib/git/src/handler/refs/get.rs lib/git/src/handler/refs/get.rs +10 −1 View lib/git/src/handler/refs/get.rs
@@ -7,13 +7,22 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct RefPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub name: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/refs/{name}",
20 tag = "refs",
21 params(RefPath),
22 responses(
23 (status = 200, description = "get ref")
24 )
25)]
1726pub async fn refs_get(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/refs/head.rs lib/git/src/handler/refs/head.rs +9 −0 View lib/git/src/handler/refs/head.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::refs::HeadInfoDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/refs/head",
13 tag = "refs",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "get head ref", body = AppResponseData<HeadInfoDto>)
17 )
18)]
1019pub async fn refs_head(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/refs/is_object_in_ref.rs lib/git/src/handler/refs/is_object_in_ref.rs +11 −2 View lib/git/src/handler/refs/is_object_in_ref.rs
@@ -7,17 +7,26 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct ContainsQuery {
1212 pub object: String,
1313 pub refname: String,
1414}
1515
16#[derive(Debug, Clone, Serialize)]
16#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1717pub struct IsObjectInRefResponse {
1818 pub contains: bool,
1919}
2020
21#[utoipa::path(
22 get,
23 path = "/repos/{space_name}/{repo_name}/refs/contains",
24 tag = "refs",
25 params(crate::handler::RepoPath, ContainsQuery),
26 responses(
27 (status = 200, description = "check object in ref", body = AppResponseData<IsObjectInRefResponse>)
28 )
29)]
2130pub async fn refs_is_object_in_ref(
2231 State(service): State<GitService>,
2332 session: Session,
Modified lib/git/src/handler/refs/list.rs lib/git/src/handler/refs/list.rs +9 −0 View lib/git/src/handler/refs/list.rs
@@ -7,6 +7,15 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/refs",
13 tag = "refs",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "list refs")
17 )
18)]
1019pub async fn refs_list(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/refs/resolve.rs lib/git/src/handler/refs/resolve.rs +10 −1 View lib/git/src/handler/refs/resolve.rs
@@ -6,13 +6,22 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct RefPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub name: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/refs/{name}/resolve",
19 tag = "refs",
20 params(RefPath),
21 responses(
22 (status = 200, description = "resolve ref", body = AppResponseData<String>)
23 )
24)]
1625pub async fn refs_resolve(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/refs/update.rs lib/git/src/handler/refs/update.rs +12 −2 View lib/git/src/handler/refs/update.rs
@@ -9,19 +9,29 @@
99use crate::service::GitService;
1010use crate::service::refs::UpdateRefResultDto;
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1313pub struct RefPath {
1414 pub space_name: String,
1515 pub repo_name: String,
1616 pub name: String,
1717}
1818
19#[derive(Debug, Clone, Serialize, Deserialize)]
19#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
2020pub struct RefUpdateBody {
2121 pub new_oid: String,
2222 pub expected_oid: Option<String>,
2323}
2424
25#[utoipa::path(
26 patch,
27 path = "/repos/{space_name}/{repo_name}/refs/{name}",
28 tag = "refs",
29 params(RefPath),
30 request_body = RefUpdateBody,
31 responses(
32 (status = 200, description = "update ref", body = AppResponseData<UpdateRefResultDto>)
33 )
34)]
2535pub async fn refs_update(
2636 State(service): State<GitService>,
2737 session: Session,
Modified lib/git/src/handler/repos/archive.rs lib/git/src/handler/repos/archive.rs +9 −0 View lib/git/src/handler/repos/archive.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::repos::RepoDto;
99
10#[utoipa::path(
11 delete,
12 path = "/repos/{space_name}/{repo_name}",
13 tag = "repos",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "archive repo", body = AppResponseData<RepoDto>)
17 )
18)]
1019pub async fn archive_repo(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/repos/create.rs lib/git/src/handler/repos/create.rs +11 −1 View lib/git/src/handler/repos/create.rs
@@ -9,11 +9,21 @@
99use crate::service::GitService;
1010use crate::service::repos::{CreateRepoCmd, RepoDto};
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1313pub struct SpacePath {
1414 pub space_name: String,
1515}
1616
17#[utoipa::path(
18 post,
19 path = "/repos/{space_name}",
20 tag = "repos",
21 params(SpacePath),
22 request_body = CreateRepoCmd,
23 responses(
24 (status = 200, description = "create repo", body = AppResponseData<RepoDto>)
25 )
26)]
1727pub async fn create_repo(
1828 State(service): State<GitService>,
1929 session: Session,
Modified lib/git/src/handler/repos/get.rs lib/git/src/handler/repos/get.rs +9 −0 View lib/git/src/handler/repos/get.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::repos::RepoDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}",
13 tag = "repos",
14 params(RepoPath),
15 responses(
16 (status = 200, description = "get repo", body = AppResponseData<RepoDto>)
17 )
18)]
1019pub async fn get_repo(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/repos/list.rs lib/git/src/handler/repos/list.rs +14 −4 View lib/git/src/handler/repos/list.rs
@@ -1,23 +1,33 @@
11use axum::extract::{Path, State};
22use export::rest::AppResponseData;
3use session::Session;
3use session::{Session, SessionUser};
44
55use crate::errors::{ApiError, GitApiError};
66use crate::service::GitService;
77use crate::service::repos::RepoDto;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct SpacePath {
1111 pub space_name: String,
1212}
1313
14#[utoipa::path(
15 get,
16 path = "/repos/{space_name}",
17 tag = "repos",
18 params(SpacePath),
19 responses(
20 (status = 200, description = "list repos", body = AppResponseData<Vec<RepoDto>>)
21 )
22)]
1423pub async fn list_repos(
1524 State(service): State<GitService>,
16 _session: Session,
25 session: Session,
1726 Path(path): Path<SpacePath>,
1827) -> Result<AppResponseData<Vec<RepoDto>>, ApiError> {
28 let caller = session.uid();
1929 let out = service
20 .list_repos(path.space_name)
30 .list_repos(path.space_name, caller)
2131 .await
2232 .map_err(GitApiError::from)?;
2333 Ok(AppResponseData::ok(out))
Modified lib/git/src/handler/repos/update.rs lib/git/src/handler/repos/update.rs +10 −0 View lib/git/src/handler/repos/update.rs
@@ -8,6 +8,16 @@
88use crate::service::GitService;
99use crate::service::repos::{RepoDto, UpdateRepoCmd};
1010
11#[utoipa::path(
12 patch,
13 path = "/repos/{space_name}/{repo_name}",
14 tag = "repos",
15 params(RepoPath),
16 request_body = UpdateRepoCmd,
17 responses(
18 (status = 200, description = "update repo", body = AppResponseData<RepoDto>)
19 )
20)]
1121pub async fn update_repo(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/submodules/list.rs lib/git/src/handler/submodules/list.rs +13 −1 View lib/git/src/handler/submodules/list.rs
@@ -7,11 +7,23 @@
77use crate::service::GitService;
88use crate::service::submodules::SubmoduleDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct SubmoduleListQuery {
1212 pub revision: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/submodules",
18 tag = "submodules",
19 params(
20 crate::handler::RepoPath,
21 SubmoduleListQuery,
22 ),
23 responses(
24 (status = 200, description = "list submodules", body = AppResponseData<Vec<SubmoduleDto>>)
25 )
26)]
1527pub async fn submodule_list(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/submodules/resolve.rs lib/git/src/handler/submodules/resolve.rs +13 −1 View lib/git/src/handler/submodules/resolve.rs
@@ -6,12 +6,24 @@
66use crate::handler::resolve_reader;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct SubmoduleResolveQuery {
1111 pub revision: String,
1212 pub path: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/submodules/resolve",
18 tag = "submodules",
19 params(
20 crate::handler::RepoPath,
21 SubmoduleResolveQuery,
22 ),
23 responses(
24 (status = 200, description = "resolve submodule oid", body = AppResponseData<String>)
25 )
26)]
1527pub async fn submodule_resolve(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/tags/create.rs lib/git/src/handler/tags/create.rs +10 −0 View lib/git/src/handler/tags/create.rs
@@ -8,6 +8,16 @@
88use crate::service::GitService;
99use crate::service::tags::{CreateTagCmd, TagDto};
1010
11#[utoipa::path(
12 post,
13 path = "/repos/{space_name}/{repo_name}/tags",
14 tag = "tags",
15 params(crate::handler::RepoPath),
16 request_body = CreateTagCmd,
17 responses(
18 (status = 200, description = "create tag", body = AppResponseData<TagDto>)
19 )
20)]
1121pub async fn create_tag(
1222 State(service): State<GitService>,
1323 session: Session,
Modified lib/git/src/handler/tags/delete.rs lib/git/src/handler/tags/delete.rs +10 −1 View lib/git/src/handler/tags/delete.rs
@@ -6,13 +6,22 @@
66use crate::handler::require_writer;
77use crate::service::GitService;
88
9#[derive(Debug, Clone, serde::Deserialize)]
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1010pub struct TagPath {
1111 pub space_name: String,
1212 pub repo_name: String,
1313 pub name: String,
1414}
1515
16#[utoipa::path(
17 delete,
18 path = "/repos/{space_name}/{repo_name}/tags/{name}",
19 tag = "tags",
20 params(TagPath),
21 responses(
22 (status = 200, description = "delete tag", body = AppResponseData<NoData>)
23 )
24)]
1625pub async fn delete_tag(
1726 State(service): State<GitService>,
1827 session: Session,
Modified lib/git/src/handler/tags/get.rs lib/git/src/handler/tags/get.rs +10 −1 View lib/git/src/handler/tags/get.rs
@@ -7,13 +7,22 @@
77use crate::service::GitService;
88use crate::service::tags::TagDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct TagPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub name: String,
1515}
1616
17#[utoipa::path(
18 get,
19 path = "/repos/{space_name}/{repo_name}/tags/{name}",
20 tag = "tags",
21 params(TagPath),
22 responses(
23 (status = 200, description = "get tag", body = AppResponseData<TagDto>)
24 )
25)]
1726pub async fn get_tag(
1827 State(service): State<GitService>,
1928 session: Session,
Modified lib/git/src/handler/tags/list.rs lib/git/src/handler/tags/list.rs +9 −0 View lib/git/src/handler/tags/list.rs
@@ -7,6 +7,15 @@
77use crate::service::GitService;
88use crate::service::tags::TagDto;
99
10#[utoipa::path(
11 get,
12 path = "/repos/{space_name}/{repo_name}/tags",
13 tag = "tags",
14 params(crate::handler::RepoPath),
15 responses(
16 (status = 200, description = "list tags", body = AppResponseData<Vec<TagDto>>)
17 )
18)]
1019pub async fn list_tags(
1120 State(service): State<GitService>,
1221 session: Session,
Modified lib/git/src/handler/tags/verify.rs lib/git/src/handler/tags/verify.rs +11 −2 View lib/git/src/handler/tags/verify.rs
@@ -7,14 +7,14 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct TagPath {
1212 pub space_name: String,
1313 pub repo_name: String,
1414 pub name: String,
1515}
1616
17#[derive(Debug, Clone, Serialize)]
17#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1818pub struct TagVerifyDto {
1919 pub valid: bool,
2020 pub signer: Option<String>,
@@ -22,6 +22,15 @@
2222 pub error_message: Option<String>,
2323}
2424
25#[utoipa::path(
26 get,
27 path = "/repos/{space_name}/{repo_name}/tags/{name}/verify",
28 tag = "tags",
29 params(TagPath),
30 responses(
31 (status = 200, description = "verify tag", body = AppResponseData<TagVerifyDto>)
32 )
33)]
2534pub async fn verify_tag(
2635 State(service): State<GitService>,
2736 session: Session,
Modified lib/git/src/handler/trees/follow.rs lib/git/src/handler/trees/follow.rs +13 −1 View lib/git/src/handler/trees/follow.rs
@@ -14,12 +14,24 @@
1414 pub target_entry: Option<TreeEntry>,
1515}
1616
17#[derive(Debug, Clone, serde::Deserialize)]
17#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1818pub struct TreeFollowQuery {
1919 pub revision: String,
2020 pub path: String,
2121}
2222
23#[utoipa::path(
24 get,
25 path = "/repos/{space_name}/{repo_name}/tree/follow",
26 tag = "trees",
27 params(
28 crate::handler::RepoPath,
29 TreeFollowQuery,
30 ),
31 responses(
32 (status = 200, description = "follow tree entry through symlink")
33 )
34)]
2335pub async fn tree_follow(
2436 State(service): State<GitService>,
2537 session: Session,
Modified lib/git/src/handler/trees/last_updated.rs lib/git/src/handler/trees/last_updated.rs +13 −1 View lib/git/src/handler/trees/last_updated.rs
@@ -7,11 +7,23 @@
77use crate::service::GitService;
88use crate::service::trees::PathLastUpdated;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct TreeLastUpdatedQuery {
1212 pub revision: String,
1313}
1414
15#[utoipa::path(
16 get,
17 path = "/repos/{space_name}/{repo_name}/tree/last-updated",
18 tag = "trees",
19 params(
20 crate::handler::RepoPath,
21 TreeLastUpdatedQuery,
22 ),
23 responses(
24 (status = 200, description = "last updated tree entries", body = AppResponseData<Vec<PathLastUpdated>>)
25 )
26)]
1527pub async fn tree_last_updated(
1628 State(service): State<GitService>,
1729 session: Session,
Modified lib/git/src/handler/trees/read.rs lib/git/src/handler/trees/read.rs +14 −2 View lib/git/src/handler/trees/read.rs
@@ -9,19 +9,31 @@
99use crate::handler::resolve_reader;
1010use crate::service::GitService;
1111
12#[derive(Debug, Clone, serde::Deserialize)]
12#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1313pub struct TreePath {
1414 pub space_name: String,
1515 pub repo_name: String,
1616 pub revision: String,
1717}
1818
19#[derive(Debug, Clone, serde::Deserialize)]
19#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
2020pub struct TreeReadQuery {
2121 #[serde(default)]
2222 pub path: Option<String>,
2323}
2424
25#[utoipa::path(
26 get,
27 path = "/repos/{space_name}/{repo_name}/tree/{revision}",
28 tag = "trees",
29 params(
30 TreePath,
31 TreeReadQuery,
32 ),
33 responses(
34 (status = 200, description = "read tree entries")
35 )
36)]
2537pub async fn tree_read(
2638 State(service): State<GitService>,
2739 session: Session,
Modified lib/git/src/handler/trees/resolve_path.rs lib/git/src/handler/trees/resolve_path.rs +13 −1 View lib/git/src/handler/trees/resolve_path.rs
@@ -7,12 +7,24 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct TreeResolveQuery {
1212 pub revision: String,
1313 pub path: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/tree/resolve",
19 tag = "trees",
20 params(
21 crate::handler::RepoPath,
22 TreeResolveQuery,
23 ),
24 responses(
25 (status = 200, description = "resolve path to tree entry")
26 )
27)]
1628pub async fn tree_resolve_path(
1729 State(service): State<GitService>,
1830 session: Session,
Modified lib/git/src/handler/trees/walk.rs lib/git/src/handler/trees/walk.rs +13 −1 View lib/git/src/handler/trees/walk.rs
@@ -7,7 +7,7 @@
77use crate::handler::resolve_reader;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct TreeWalkQuery {
1212 pub revision: String,
1313 #[serde(default)]
@@ -16,6 +16,18 @@
1616 pub show_trees: Option<bool>,
1717}
1818
19#[utoipa::path(
20 get,
21 path = "/repos/{space_name}/{repo_name}/tree/walk",
22 tag = "trees",
23 params(
24 crate::handler::RepoPath,
25 TreeWalkQuery,
26 ),
27 responses(
28 (status = 200, description = "walk tree")
29 )
30)]
1931pub async fn tree_walk(
2032 State(service): State<GitService>,
2133 session: Session,
Modified lib/git/src/handler/blame.rs lib/git/src/handler/blame.rs +13 −1 View lib/git/src/handler/blame.rs
@@ -7,7 +7,7 @@
77use crate::service::blame::BlameRangeDto;
88use crate::service::GitService;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct BlameQuery {
1212 pub revision: String,
1313 pub path: String,
@@ -17,6 +17,18 @@
1717 pub range_end: Option<u32>,
1818}
1919
20#[utoipa::path(
21 get,
22 path = "/repos/{space_name}/{repo_name}/blame",
23 tag = "blame",
24 params(
25 crate::handler::RepoPath,
26 BlameQuery,
27 ),
28 responses(
29 (status = 200, description = "blame ranges", body = AppResponseData<Vec<BlameRangeDto>>)
30 )
31)]
2032pub async fn blame(
2133 State(service): State<GitService>,
2234 session: Session,
Modified lib/git/src/handler/graphql.rs lib/git/src/handler/graphql.rs +22 −1 View lib/git/src/handler/graphql.rs
@@ -1,20 +1,41 @@
11use axum::Json;
22use axum::extract::State;
33use axum::response::IntoResponse;
4use session::{Session, SessionUser};
5use uuid::Uuid;
46
57use crate::errors::ApiError;
68use crate::graphql::{build_schema, GitSchema};
79use crate::service::GitService;
810
11#[utoipa::path(
12 post,
13 path = "/graphql",
14 tag = "graphql",
15 request_body = String,
16 responses(
17 (status = 200, description = "graphql execution result")
18 )
19)]
920pub async fn graphql_post(
1021 State(_service): State<GitService>,
22 session: Session,
1123 schema: axum::extract::Extension<GitSchema>,
12 Json(request): Json<async_graphql::Request>,
24 Json(mut request): Json<async_graphql::Request>,
1325) -> Result<axum::response::Response, ApiError> {
26 request = request.data::<Option<Uuid>>(session.uid());
1427 let response = schema.execute(request).await;
1528 Ok(Json(response).into_response())
1629}
1730
31#[utoipa::path(
32 get,
33 path = "/graphql",
34 tag = "graphql",
35 responses(
36 (status = 200, description = "graphql execution result")
37 )
38)]
1839pub async fn graphql_get(
1940 State(_service): State<GitService>,
2041 schema: axum::extract::Extension<GitSchema>,
Modified lib/git/src/handler/path_log.rs lib/git/src/handler/path_log.rs +13 −1 View lib/git/src/handler/path_log.rs
@@ -7,12 +7,24 @@
77use crate::service::GitService;
88use crate::service::path_log::PathLogEntryDto;
99
10#[derive(Debug, Clone, serde::Deserialize)]
10#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1111pub struct PathLogQuery {
1212 pub commit_hash: String,
1313 pub target_dir: String,
1414}
1515
16#[utoipa::path(
17 get,
18 path = "/repos/{space_name}/{repo_name}/path-log",
19 tag = "logs",
20 params(
21 crate::handler::RepoPath,
22 PathLogQuery,
23 ),
24 responses(
25 (status = 200, description = "path log entries", body = AppResponseData<Vec<PathLogEntryDto>>)
26 )
27)]
1628pub async fn path_log(
1729 State(service): State<GitService>,
1830 session: Session,
Modified lib/git/src/lfs/types.rs lib/git/src/lfs/types.rs +4 −4 View lib/git/src/lfs/types.rs
@@ -86,12 +86,12 @@
8686 pub size: i64,
8787}
8888
89#[derive(Debug, Clone, Serialize, Deserialize)]
89#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
9090pub struct LfsLockOwner {
9191 pub name: String,
9292}
9393
94#[derive(Debug, Clone, Serialize, Deserialize)]
94#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
9595pub struct LfsLock {
9696 pub id: String,
9797 pub path: String,
@@ -106,7 +106,7 @@
106106 pub r#ref: Option<LfsRef>,
107107}
108108
109#[derive(Debug, Clone, Serialize, Deserialize)]
109#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
110110pub struct LfsLockListResponse {
111111 #[serde(default)]
112112 pub locks: Vec<LfsLock>,
@@ -122,7 +122,7 @@
122122 pub limit: Option<u32>,
123123}
124124
125#[derive(Debug, Clone, Serialize)]
125#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
126126pub struct LfsVerifyLocksResponse {
127127 #[serde(default)]
128128 pub ours: Vec<LfsLock>,
Modified lib/git/src/service/analysis.rs lib/git/src/service/analysis.rs +4 −4 View lib/git/src/service/analysis.rs
@@ -13,7 +13,7 @@
1313use crate::service::GitService;
1414use crate::share::errors::{GitError, map_baby};
1515
16#[derive(Debug, Clone, Serialize)]
16#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1717pub struct LanguageStatDto {
1818 pub language: String,
1919 pub percentage_bp: i32,
@@ -21,7 +21,7 @@
2121 pub is_primary: bool,
2222}
2323
24#[derive(Debug, Clone, Serialize)]
24#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2525pub struct ContributorStatDto {
2626 pub name: String,
2727 pub email: String,
@@ -30,7 +30,7 @@
3030 pub deletions: i64,
3131}
3232
33#[derive(Debug, Clone, Serialize)]
33#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
3434pub struct CodeActivityAuthorDto {
3535 pub name: String,
3636 pub email: String,
@@ -39,7 +39,7 @@
3939 pub deletions: i64,
4040}
4141
42#[derive(Debug, Clone, Serialize)]
42#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
4343pub struct CodeActivityDto {
4444 pub authors: Vec<CodeActivityAuthorDto>,
4545 pub total_commits: i64,
Modified lib/git/src/service/archives.rs lib/git/src/service/archives.rs +62 −38 View lib/git/src/service/archives.rs
@@ -1,5 +1,4 @@
11use std::path::PathBuf;
2use std::process::Command;
32
43use export::storage::AppStorage;
54use gitbaby::archive::types::{ArchiveFormat, CreateArchiveRequest};
@@ -7,9 +6,12 @@
76use time::OffsetDateTime;
87
98use crate::service::GitService;
9use crate::service::cache::sanitize_key;
10use crate::service::gitcmd::{run_git, run_git_capped};
1011use crate::share::errors::{GitError, map_baby};
12use crate::share::validate::validate_revision;
1113
12#[derive(Debug, Clone, Serialize)]
14#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1315pub struct ArchiveResultDto {
1416 pub format: String,
1517 pub size: i64,
@@ -41,6 +43,7 @@
4143 revision: String,
4244 opts: ArchiveOptionsDto,
4345 ) -> Result<ArchiveResultDto, GitError> {
46 validate_revision(&revision)?;
4447 let ctx = self.context(space_name, repo_name).await?;
4548 let baby = self.baby(&ctx);
4649 let ext = fmt_ext(opts.format);
@@ -53,10 +56,16 @@
5356 };
5457 baby.create_archive(req).await.map_err(map_baby)?;
5558
56 let key = format!("git/{}/{}.{}", ctx.model.id.simple(), revision, ext);
59 let key = format!(
60 "git/{}/{}.{}",
61 ctx.model.id.simple(),
62 sanitize_key(&revision),
63 ext
64 );
5765 let out_dir = ctx.repo_dir();
5866 let storage: &AppStorage = &self.state.storage;
59 let bytes = read_archive_bytes(&out_dir, &revision, ext)?;
67 let timeout = self.state.config.git.git_command_timeout_secs;
68 let bytes = read_archive_bytes(&out_dir, &revision, ext, timeout).await?;
6069 let bytes_len = bytes.len() as i64;
6170 storage
6271 .client()
@@ -80,9 +89,15 @@
8089 revision: String,
8190 opts: ArchiveOptionsDto,
8291 ) -> Result<ArchiveResultDto, GitError> {
92 validate_revision(&revision)?;
8393 let ctx = self.context(space_name, repo_name).await?;
8494 let ext = fmt_ext(opts.format);
85 let key = format!("git/{}/{}.{}", ctx.model.id.simple(), revision, ext);
95 let key = format!(
96 "git/{}/{}.{}",
97 ctx.model.id.simple(),
98 sanitize_key(&revision),
99 ext
100 );
86101 let storage: &AppStorage = &self.state.storage;
87102 let exists = storage
88103 .client()
@@ -112,27 +127,29 @@
112127 std::process::id(),
113128 OffsetDateTime::now_utc().unix_timestamp_nanos()
114129 ));
115 let out = Command::new("git")
116 .arg("-C")
117 .arg(ctx.repo_dir())
118 .args([
119 "bundle",
120 "create",
121 bundle_path.to_str().unwrap_or("bundle.git"),
122 "--all",
123 ])
124 .output()
125 .map_err(|e| GitError::Internal(format!("bundle create: {e}")))?;
126 if !out.status.success() {
127 return Err(GitError::Internal(format!(
128 "bundle failed: {}",
129 String::from_utf8_lossy(&out.stderr).trim()
130 )));
131 }
130 let timeout = self.state.config.git.git_command_timeout_secs;
131 run_git(
132 "archives::bundle",
133 &ctx.repo_dir(),
134 vec![
135 "bundle".to_string(),
136 "create".to_string(),
137 bundle_path.to_string_lossy().into_owned(),
138 "--all".to_string(),
139 ],
140 &[],
141 timeout,
142 )
143 .await?;
132144 let key = format!("git/{}/bundle.git", ctx.model.id.simple());
133145 let storage: &AppStorage = &self.state.storage;
134 let bytes = std::fs::read(&bundle_path)
135 .map_err(|e| GitError::Internal(format!("bundle read: {e}")))?;
146 let bytes = tokio::task::spawn_blocking({
147 let p = bundle_path.clone();
148 move || std::fs::read(&p)
149 })
150 .await
151 .map_err(|e| GitError::Internal(format!("bundle read task: {e}")))?
152 .map_err(|e| GitError::Internal(format!("bundle read: {e}")))?;
136153 let bytes_len = bytes.len() as i64;
137154 storage
138155 .client()
@@ -150,19 +167,26 @@
150167 }
151168}
152169
153fn read_archive_bytes(repo_dir: &PathBuf, rev: &str, ext: &str) -> Result<Vec<u8>, GitError> {
154 use std::process::Command;
155 let out = Command::new("git")
156 .arg("-C")
157 .arg(repo_dir)
158 .args(["archive", "--format", ext, rev])
159 .output()
160 .map_err(|e| GitError::Internal(format!("archive read failed: {e}")))?;
161 if !out.status.success() {
162 return Err(GitError::Internal(format!(
163 "archive failed: {}",
164 String::from_utf8_lossy(&out.stderr).trim()
165 )));
166 }
170async fn read_archive_bytes(
171 dir: &PathBuf,
172 rev: &str,
173 ext: &str,
174 timeout_secs: u64,
175) -> Result<Vec<u8>, GitError> {
176 let cap = 512 * 1024 * 1024;
177 let out = run_git_capped(
178 "archives::read",
179 dir,
180 vec![
181 "archive".to_string(),
182 "--format".to_string(),
183 ext.to_string(),
184 rev.to_string(),
185 ],
186 &[],
187 timeout_secs,
188 cap,
189 )
190 .await?;
167191 Ok(out.stdout)
168192}
Modified lib/git/src/service/branches.rs lib/git/src/service/branches.rs +2 −2 View lib/git/src/service/branches.rs
@@ -13,7 +13,7 @@
1313use crate::share::policy::load_branch_policy;
1414use crate::share::validate::validate_branch_name;
1515
16#[derive(Debug, Clone, Serialize)]
16#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1717pub struct BranchDto {
1818 pub name: String,
1919 pub commit_sha: String,
@@ -38,7 +38,7 @@
3838 }
3939}
4040
41#[derive(Debug, Clone, Deserialize)]
41#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
4242pub struct CreateBranchCmd {
4343 pub name: String,
4444 #[serde(default)]
Modified lib/git/src/service/commits.rs lib/git/src/service/commits.rs +3 −3 View lib/git/src/service/commits.rs
@@ -37,7 +37,7 @@
3737 pub first_parent: bool,
3838}
3939
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
4141#[serde(rename_all = "lowercase")]
4242pub enum CommitStatusDtoState {
4343 Pending,
@@ -68,7 +68,7 @@
6868 }
6969}
7070
71#[derive(Debug, Clone, Deserialize)]
71#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
7272pub struct SetCommitStatusCmd {
7373 pub sha: String,
7474 pub state: CommitStatusDtoState,
@@ -79,7 +79,7 @@
7979 pub target_url: Option<String>,
8080}
8181
82#[derive(Debug, Clone, Serialize)]
82#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
8383pub struct CommitStatusDto {
8484 pub state: CommitStatusDtoState,
8585 pub context: String,
Modified lib/git/src/service/conflicts.rs lib/git/src/service/conflicts.rs +2 −2 View lib/git/src/service/conflicts.rs
@@ -4,14 +4,14 @@
44use crate::service::GitService;
55use crate::share::errors::{GitError, map_baby};
66
7#[derive(Debug, Clone, Serialize)]
7#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
88pub struct MergeStatusDto {
99 pub mergeable: bool,
1010 pub conflicts: Vec<String>,
1111 pub fast_forwardable: bool,
1212}
1313
14#[derive(Debug, Clone, Serialize)]
14#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1515pub struct ConflictDetailDto {
1616 pub path: String,
1717 pub content: Option<String>,
Modified lib/git/src/service/content.rs lib/git/src/service/content.rs +19 −12 View lib/git/src/service/content.rs
@@ -15,28 +15,28 @@
1515use crate::share::errors::{GitError, map_baby};
1616use crate::share::identity::load_identity;
1717
18#[derive(Debug, Clone, Serialize, Deserialize)]
18#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
1919pub struct SignatureDto {
2020 pub name: String,
2121 pub email: String,
2222 pub when: OffsetDateTime,
2323}
2424
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
2626#[serde(rename_all = "lowercase")]
2727pub enum ObjectKind {
2828 Merge,
2929 Squash,
3030}
3131
32#[derive(Debug, Clone, Serialize, Deserialize)]
32#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
3333pub struct FileEntryDto {
3434 pub path: String,
3535 pub content: Vec<u8>,
3636 pub executable: bool,
3737}
3838
39#[derive(Debug, Clone, Serialize)]
39#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
4040pub struct WriteObjectResultDto {
4141 pub id: String,
4242}
@@ -168,6 +168,8 @@
168168 if p.is_absolute()
169169 || p.components()
170170 .any(|c| matches!(c, std::path::Component::ParentDir))
171 || path.contains('\\')
172 || path.chars().any(|c| c.is_control())
171173 {
172174 return Err(GitError::InvalidArgument(format!(
173175 "invalid relative path `{path}`"
@@ -179,7 +181,7 @@
179181fn tree_entry_bytes(mode_octal: u32, name: &str, oid: ObjectId) -> Vec<u8> {
180182 let mut buf = Vec::with_capacity(64 + name.len());
181183 use std::io::Write as _;
182 write!(&mut buf, "{:o} ", mode_octal).unwrap();
184 let _ = write!(&mut buf, "{:o} ", mode_octal);
183185 buf.extend_from_slice(name.as_bytes());
184186 buf.push(0);
185187 buf.extend_from_slice(oid.as_bytes());
@@ -211,19 +213,24 @@
211213 entry.insert(path.clone(), *oid);
212214 }
213215 }
214 let mut buf: Vec<u8> = Vec::new();
216 let mut entries: Vec<(String, ObjectId)> = Vec::new();
215217 for (name, oid) in &direct {
216 buf.extend_from_slice(&tree_entry_bytes(0o100644, name, *oid));
218 entries.push((name.clone(), *oid));
217219 }
218 let mut sub_meta: Vec<(String, ObjectId)> = Vec::new();
219220 for (dir_name, sub_blobs) in &dir_blobs {
220221 let sub_prefix = format!("{prefix}{dir_name}/");
221222 let sub_oid = build_tree_recursive_sync(&sub_prefix, sub_blobs, write_tree)?;
222 sub_meta.push((dir_name.clone(), sub_oid));
223 entries.push((format!("{dir_name}/"), sub_oid));
223224 }
224 sub_meta.sort_by(|a, b| a.0.cmp(&b.0));
225 for (name, oid) in &sub_meta {
226 buf.extend_from_slice(&tree_entry_bytes(0o040000, name, *oid));
225 entries.sort_by(|a, b| a.0.cmp(&b.0));
226 let mut buf: Vec<u8> = Vec::new();
227 for (name, oid) in entries {
228 let (mode, name) = if let Some(plain) = name.strip_suffix('/') {
229 (0o040000u32, plain)
230 } else {
231 (0o100644u32, name.as_str())
232 };
233 buf.extend_from_slice(&tree_entry_bytes(mode, name, oid));
227234 }
228235 let mut object = format!("tree {}\0", buf.len()).into_bytes();
229236 object.extend_from_slice(&buf);
Modified lib/git/src/service/hooks.rs lib/git/src/service/hooks.rs +13 −9 View lib/git/src/service/hooks.rs
@@ -1,6 +1,7 @@
11use serde::{Deserialize, Serialize};
22
33use crate::service::GitService;
4use crate::service::gitcmd::run_exec_cmd;
45use crate::share::errors::GitError;
56
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
@@ -36,7 +37,7 @@
3637 pub message: Option<String>,
3738}
3839
39#[derive(Debug, Clone, Serialize)]
40#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
4041pub struct ScriptOutputDto {
4142 pub exit_code: i32,
4243 pub stdout: String,
@@ -81,14 +82,17 @@
8182 }
8283 let ctx = self.context(space_name, repo_name).await?;
8384 let dir = ctx.repo_dir().join("hooks");
84 let mut cmd = std::process::Command::new(dir.join(&script));
85 for a in &args {
86 cmd.arg(a);
87 }
88 let out = tokio::task::spawn_blocking(move || cmd.output())
89 .await
90 .map_err(|e| GitError::Internal(format!("script spawn: {e}")))?
91 .map_err(|e| GitError::Internal(format!("script exec: {e}")))?;
85 let prog = dir.join(&script);
86 let timeout = self.state.config.git.git_command_timeout_secs;
87 let out = run_exec_cmd(
88 "hooks::execute_script",
89 &prog.to_string_lossy(),
90 &dir,
91 &args,
92 timeout,
93 64 * 1024 * 1024,
94 )
95 .await?;
9296 Ok(ScriptOutputDto {
9397 exit_code: out.status.code().unwrap_or(-1),
9498 stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
Modified lib/git/src/service/logs.rs lib/git/src/service/logs.rs +1 −1 View lib/git/src/service/logs.rs
@@ -10,7 +10,7 @@
1010 pub has_more: bool,
1111}
1212
13#[derive(Debug, Clone, serde::Deserialize)]
13#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1414pub struct LogOptions {
1515 #[serde(default)]
1616 pub revision: Option<String>,
Modified lib/git/src/service/maintenance.rs lib/git/src/service/maintenance.rs +21 −16 View lib/git/src/service/maintenance.rs
@@ -1,18 +1,17 @@
1use std::process::Command;
2
31use serde::Serialize;
42
53use crate::service::GitService;
4use crate::service::gitcmd::run_git_soft;
65use crate::share::errors::{GitError, map_baby};
76
8#[derive(Debug, Clone, Serialize)]
7#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
98pub struct PruneResultDto {
109 pub reachable: i64,
1110 pub unreachable: i64,
1211 pub total: i64,
1312}
1413
15#[derive(Debug, Clone, Serialize)]
14#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1615pub struct FsckDto {
1716 pub ok: bool,
1817 pub dangling: i64,
@@ -27,12 +26,15 @@
2726 ) -> Result<PruneResultDto, GitError> {
2827 let ctx = self.context(space_name, repo_name).await?;
2928 self.baby(&ctx).prune().await.map_err(map_baby)?;
30 let out = Command::new("git")
31 .arg("-C")
32 .arg(ctx.repo_dir())
33 .args(["count-objects", "-v"])
34 .output()
35 .map_err(|e| GitError::Internal(format!("count-objects: {e}")))?;
29 let timeout = self.state.config.git.git_command_timeout_secs;
30 let out = run_git_soft(
31 "maintenance::count_objects",
32 &ctx.repo_dir(),
33 vec!["count-objects".to_string(), "-v".to_string()],
34 &[],
35 timeout,
36 )
37 .await?;
3638 let mut total: i64 = 0;
3739 if out.status.success() {
3840 for line in String::from_utf8_lossy(&out.stdout).lines() {
@@ -54,12 +56,15 @@
5456 repo_name: String,
5557 ) -> Result<FsckDto, GitError> {
5658 let ctx = self.context(space_name, repo_name).await?;
57 let out = Command::new("git")
58 .arg("-C")
59 .arg(ctx.repo_dir())
60 .args(["fsck", "--no-progress", "--no-dangling"])
61 .output()
62 .map_err(|e| GitError::Internal(format!("fsck spawn: {e}")))?;
59 let timeout = self.state.config.git.git_command_timeout_secs;
60 let out = run_git_soft(
61 "maintenance::fsck",
62 &ctx.repo_dir(),
63 vec!["fsck".to_string(), "--no-progress".to_string()],
64 &[],
65 timeout,
66 )
67 .await?;
6368 let stdout = String::from_utf8_lossy(&out.stdout);
6469 let dangling = stdout.lines().filter(|l| l.contains("dangling")).count() as i64;
6570 Ok(FsckDto {
Modified lib/git/src/service/packs.rs lib/git/src/service/packs.rs +36 −52 View lib/git/src/service/packs.rs
@@ -1,17 +1,16 @@
1use std::process::Command;
2
31use serde::Serialize;
42
53use crate::service::GitService;
4use crate::service::gitcmd::{run_git, run_git_soft, run_git_with_stdin};
65use crate::share::errors::{GitError, map_baby};
76
8#[derive(Debug, Clone, Serialize)]
7#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
98pub struct IngestPackDto {
109 pub object_count: i64,
1110 pub size: i64,
1211}
1312
14#[derive(Debug, Clone, Serialize)]
13#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1514pub struct PackStatsDto {
1615 pub loose_count: i64,
1716 pub size_bytes: i64,
@@ -25,30 +24,16 @@
2524 pack: Vec<u8>,
2625 ) -> Result<IngestPackDto, GitError> {
2726 let ctx = self.context(space_name, repo_name).await?;
28 let dir = ctx.repo_dir();
29 let mut cmd = Command::new("git");
30 cmd.arg("-C").arg(&dir).args(["index-pack", "--stdin"]);
31 let mut child = cmd
32 .stdin(std::process::Stdio::piped())
33 .stdout(std::process::Stdio::piped())
34 .stderr(std::process::Stdio::piped())
35 .spawn()
36 .map_err(|e| GitError::Internal(format!("index-pack spawn: {e}")))?;
37 if let Some(mut stdin) = child.stdin.take() {
38 use std::io::Write;
39 stdin
40 .write_all(&pack)
41 .map_err(|e| GitError::Internal(format!("index-pack write: {e}")))?;
42 }
43 let out = child
44 .wait_with_output()
45 .map_err(|e| GitError::Internal(format!("index-pack wait: {e}")))?;
46 if !out.status.success() {
47 return Err(GitError::Internal(format!(
48 "index-pack failed: {}",
49 String::from_utf8_lossy(&out.stderr).trim()
50 )));
51 }
27 let timeout = self.state.config.git.git_command_timeout_secs;
28 let out = run_git_with_stdin(
29 "packs::ingest",
30 &ctx.repo_dir(),
31 vec!["index-pack".to_string(), "--stdin".to_string()],
32 &[],
33 &pack,
34 timeout,
35 )
36 .await?;
5237 let mut object_count: i64 = 0;
5338 for line in String::from_utf8_lossy(&out.stdout).lines() {
5439 if let Some(rest) = line.strip_prefix("non delta: ")
@@ -63,18 +48,20 @@
6348
6449 pub async fn pack_repack(&self, space_name: String, repo_name: String) -> Result<(), GitError> {
6550 let ctx = self.context(space_name, repo_name).await?;
66 let out = Command::new("git")
67 .arg("-C")
68 .arg(ctx.repo_dir())
69 .args(["repack", "-a", "-d", "-q"])
70 .output()
71 .map_err(|e| GitError::Internal(format!("repack: {e}")))?;
72 if !out.status.success() {
73 return Err(GitError::Internal(format!(
74 "repack failed: {}",
75 String::from_utf8_lossy(&out.stderr).trim()
76 )));
77 }
51 let timeout = self.state.config.git.git_command_timeout_secs;
52 run_git(
53 "packs::repack",
54 &ctx.repo_dir(),
55 vec![
56 "repack".to_string(),
57 "-a".to_string(),
58 "-d".to_string(),
59 "-q".to_string(),
60 ],
61 &[],
62 timeout,
63 )
64 .await?;
7865 Ok(())
7966 }
8067
@@ -84,18 +71,15 @@
8471 repo_name: String,
8572 ) -> Result<PackStatsDto, GitError> {
8673 let ctx = self.context(space_name, repo_name).await?;
87 let out = Command::new("git")
88 .arg("-C")
89 .arg(ctx.repo_dir())
90 .args(["count-objects", "-v"])
91 .output()
92 .map_err(|e| GitError::Internal(format!("count-objects: {e}")))?;
93 if !out.status.success() {
94 return Err(GitError::Internal(format!(
95 "count-objects failed: {}",
96 String::from_utf8_lossy(&out.stderr).trim()
97 )));
98 }
74 let timeout = self.state.config.git.git_command_timeout_secs;
75 let out = run_git_soft(
76 "packs::stats",
77 &ctx.repo_dir(),
78 vec!["count-objects".to_string(), "-v".to_string()],
79 &[],
80 timeout,
81 )
82 .await?;
9983 let mut loose_count: i64 = 0;
10084 let mut size: i64 = 0;
10185 for line in String::from_utf8_lossy(&out.stdout).lines() {
Modified lib/git/src/service/patches.rs lib/git/src/service/patches.rs +18 −20 View lib/git/src/service/patches.rs
@@ -1,24 +1,23 @@
1use std::process::Command;
2
31use serde::Serialize;
42use time::OffsetDateTime;
53
64use crate::service::GitService;
5use crate::service::gitcmd::run_git_soft;
76use crate::share::errors::{GitError, map_baby};
8#[derive(Debug, Clone, Serialize)]
7#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
98pub struct PatchDto {
109 pub content: String,
1110 pub size: i64,
1211}
1312
14#[derive(Debug, Clone, Serialize)]
13#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1514pub struct ParsedPatchDto {
1615 pub files: Vec<String>,
1716 pub additions: i64,
1817 pub deletions: i64,
1918}
2019
21#[derive(Debug, Clone, Serialize)]
20#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2221pub struct AppliedPatchDto {
2322 pub applied: bool,
2423 pub conflicts: Vec<String>,
@@ -103,21 +102,20 @@
103102 tokio::fs::write(&patch_path, patch.as_bytes())
104103 .await
105104 .map_err(GitError::from)?;
106 let out = tokio::task::spawn_blocking({
107 let dir = dir.clone();
108 let patch = patch_path.clone();
109 move || {
110 Command::new("git")
111 .arg("-C")
112 .arg(&dir)
113 .args(["apply", "--check", "--3way"])
114 .arg(&patch)
115 .output()
116 }
117 })
118 .await
119 .map_err(|e| GitError::Internal(format!("apply task: {e}")))?
120 .map_err(|e| GitError::Internal(format!("apply failed: {e}")))?;
105 let timeout = self.state.config.git.git_command_timeout_secs;
106 let out = run_git_soft(
107 "patches::apply",
108 &dir,
109 vec![
110 "apply".to_string(),
111 "--check".to_string(),
112 "--3way".to_string(),
113 patch_path.to_string_lossy().into_owned(),
114 ],
115 &[],
116 timeout,
117 )
118 .await?;
121119 let _ = base_oid;
122120 let applied = out.status.success();
123121 let conflicts = if applied {
Modified lib/git/src/service/pipelines.rs lib/git/src/service/pipelines.rs +3 −3 View lib/git/src/service/pipelines.rs
@@ -5,7 +5,7 @@
55use crate::service::GitService;
66use crate::share::errors::GitError;
77
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
99#[serde(rename_all = "lowercase")]
1010pub enum CommitStatusState {
1111 Pending,
@@ -25,7 +25,7 @@
2525 pub creator: Option<String>,
2626}
2727
28#[derive(Debug, Clone, Serialize, Deserialize)]
28#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
2929pub struct JobDto {
3030 pub id: String,
3131 pub name: String,
@@ -34,7 +34,7 @@
3434 pub finished_at: Option<OffsetDateTime>,
3535}
3636
37#[derive(Debug, Clone, Serialize)]
37#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
3838pub struct TriggerResultDto {
3939 pub job_id: String,
4040 pub queued: bool,
Modified lib/git/src/service/refs.rs lib/git/src/service/refs.rs +3 −3 View lib/git/src/service/refs.rs
@@ -6,7 +6,7 @@
66use crate::share::policy::load_branch_policy;
77use crate::share::validate::validate_qualified_ref;
88
9#[derive(Debug, Clone, Serialize)]
9#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1010pub struct HeadInfoDto {
1111 pub name: String,
1212 pub object: String,
@@ -14,7 +14,7 @@
1414 pub is_detached: bool,
1515}
1616
17#[derive(Debug, Clone, Serialize)]
17#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1818pub struct BranchInfoDto {
1919 pub name: String,
2020 pub commit_sha: String,
@@ -22,7 +22,7 @@
2222 pub is_protected: bool,
2323}
2424
25#[derive(Debug, Clone, Serialize)]
25#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2626pub struct UpdateRefResultDto {
2727 pub name: String,
2828 pub old_oid: Option<String>,
Modified lib/git/src/service/remotes.rs lib/git/src/service/remotes.rs +69 −63 View lib/git/src/service/remotes.rs
@@ -1,29 +1,29 @@
11use serde::{Deserialize, Serialize};
2use std::process::Command;
32
43use crate::service::GitService;
4use crate::service::gitcmd::{run_git, run_git_soft};
55use crate::share::errors::{GitError, map_baby};
6use crate::share::validate::validate_remote_url;
6use crate::share::validate::{validate_refspec, validate_remote_name, validate_remote_url};
77
8#[derive(Debug, Clone, Serialize)]
8#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
99pub struct ImportResultDto {
1010 pub default_branch: String,
1111 pub commit_count: i64,
1212}
1313
14#[derive(Debug, Clone, Serialize)]
14#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
1515pub struct FetchResultDto {
1616 pub updated: Vec<String>,
1717 pub new: Vec<String>,
1818}
1919
20#[derive(Debug, Clone, Serialize)]
20#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2121pub struct PushResultDto {
2222 pub pushed: Vec<String>,
2323 pub rejected: Vec<String>,
2424}
2525
26#[derive(Debug, Clone, Deserialize)]
26#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
2727pub struct RemoteAuthDto {
2828 #[serde(default)]
2929 pub token: Option<String>,
@@ -51,27 +51,15 @@
5151 validate_remote_url(&url)?;
5252 let ctx = self.context(space_name, repo_name).await?;
5353 let dir = ctx.repo_dir();
54 let out = tokio::task::spawn_blocking({
55 let dir = dir.clone();
56 let url = url.clone();
57 move || {
58 Command::new("git")
59 .arg("-C")
60 .arg(&dir)
61 .args(["fetch", "--mirror"])
62 .arg(&url)
63 .output()
64 }
65 })
66 .await
67 .map_err(|e| GitError::Internal(format!("import task: {e}")))?
68 .map_err(|e| GitError::Internal(format!("fetch mirror failed: {e}")))?;
69 if !out.status.success() {
70 return Err(GitError::Internal(format!(
71 "import failed: {}",
72 String::from_utf8_lossy(&out.stderr).trim()
73 )));
74 }
54 let timeout = self.state.config.git.git_command_timeout_secs;
55 run_git(
56 "remotes::import",
57 &dir,
58 vec!["fetch".to_string(), "--mirror".to_string(), url],
59 &[],
60 timeout,
61 )
62 .await?;
7563 let baby = self.baby(&ctx);
7664 let default = baby.get_default_branch().await.map_err(map_baby)?;
7765 let commit_count = count_refs(&dir).await?;
@@ -89,19 +77,28 @@
8977 auth: Option<RemoteAuthDto>,
9078 ) -> Result<FetchResultDto, GitError> {
9179 let ctx = self.context(space_name, repo_name).await?;
92 let mut cmd = Command::new("git");
93 cmd.arg("-C").arg(ctx.repo_dir());
80 validate_remote_name(&remote)?;
81 let mut env: Vec<(String, String)> = Vec::new();
9482 if let Some(a) = &auth
9583 && let Some(t) = &a.token
9684 {
97 cmd.env("GIT_ASKPASS", "/bin/true");
98 cmd.env("GIT_TOKEN", t);
85 env.push(("GIT_ASKPASS".to_string(), "/bin/true".to_string()));
86 env.push(("GIT_TOKEN".to_string(), t.clone()));
9987 }
100 cmd.args(["fetch", "--prune", &remote]);
101 let out = tokio::task::spawn_blocking(move || cmd.output())
102 .await
103 .map_err(|e| GitError::Internal(format!("fetch task: {e}")))?
104 .map_err(|e| GitError::Internal(format!("fetch failed: {e}")))?;
88 let timeout = self.state.config.git.git_command_timeout_secs;
89 let out = run_git(
90 "remotes::fetch",
91 &ctx.repo_dir(),
92 vec![
93 "fetch".to_string(),
94 "--prune".to_string(),
95 "--".to_string(),
96 remote,
97 ],
98 &env,
99 timeout,
100 )
101 .await?;
105102 let updated: Vec<String> = String::from_utf8_lossy(&out.stdout)
106103 .lines()
107104 .filter_map(|l| l.split_whitespace().nth(1).map(|s| s.to_string()))
@@ -121,22 +118,28 @@
121118 auth: Option<RemoteAuthDto>,
122119 ) -> Result<PushResultDto, GitError> {
123120 let ctx = self.context(space_name, repo_name).await?;
124 let mut cmd = Command::new("git");
125 cmd.arg("-C").arg(ctx.repo_dir());
121 validate_remote_name(&remote)?;
122 let mut env: Vec<(String, String)> = Vec::new();
126123 if let Some(a) = &auth
127124 && let Some(t) = &a.token
128125 {
129 cmd.env("GIT_ASKPASS", "/bin/true");
130 cmd.env("GIT_TOKEN", t);
126 env.push(("GIT_ASKPASS".to_string(), "/bin/true".to_string()));
127 env.push(("GIT_TOKEN".to_string(), t.clone()));
131128 }
132 cmd.args(["push", &remote]);
129 let mut args = vec!["push".to_string(), "--".to_string(), remote];
133130 if let Some(r) = refspec {
134 cmd.arg(r);
131 validate_refspec(&r)?;
132 args.push(r);
135133 }
136 let out = tokio::task::spawn_blocking(move || cmd.output())
137 .await
138 .map_err(|e| GitError::Internal(format!("push task: {e}")))?
139 .map_err(|e| GitError::Internal(format!("push failed: {e}")))?;
134 let timeout = self.state.config.git.git_command_timeout_secs;
135 let out = run_git_soft(
136 "remotes::push",
137 &ctx.repo_dir(),
138 args,
139 &env,
140 timeout,
141 )
142 .await?;
140143 let stdout = String::from_utf8_lossy(&out.stdout);
141144 let pushed: Vec<String> = stdout
142145 .lines()
@@ -168,6 +171,7 @@
168171 name: String,
169172 url: String,
170173 ) -> Result<(), GitError> {
174 validate_remote_name(&name)?;
171175 validate_remote_url(&url)?;
172176 let ctx = self.context(space_name, repo_name).await?;
173177 self.baby(&ctx)
@@ -182,27 +186,29 @@
182186 repo_name: String,
183187 name: String,
184188 ) -> Result<(), GitError> {
189 validate_remote_name(&name)?;
185190 let ctx = self.context(space_name, repo_name).await?;
186191 self.baby(&ctx).remove_remote(&name).await.map_err(map_baby)
187192 }
188193}
189194
190195async fn count_refs(dir: &std::path::Path) -> Result<i64, GitError> {
191 let dir = dir.to_path_buf();
192 let total = tokio::task::spawn_blocking(move || {
193 let out = Command::new("git")
194 .arg("-C")
195 .arg(&dir)
196 .args(["for-each-ref", "--count", "--format=%(objectname)"])
197 .output();
198 match out {
199 Ok(o) if o.status.success() => {
200 String::from_utf8_lossy(&o.stdout).lines().count() as i64
201 }
202 _ => 0,
203 }
204 })
205 .await
206 .map_err(|e| GitError::Internal(format!("count task: {e}")))?;
207 Ok(total)
196 let timeout = 60;
197 let out = run_git_soft(
198 "remotes::count_refs",
199 dir,
200 vec![
201 "for-each-ref".to_string(),
202 "--count".to_string(),
203 "--format=%(objectname)".to_string(),
204 ],
205 &[],
206 timeout,
207 )
208 .await?;
209 if out.status.success() {
210 Ok(String::from_utf8_lossy(&out.stdout).lines().count() as i64)
211 } else {
212 Ok(0)
213 }
208214}
Modified lib/git/src/service/repos.rs lib/git/src/service/repos.rs +64 −37 View lib/git/src/service/repos.rs
@@ -5,11 +5,12 @@
55use gitbaby::refs::types::{ListRefsOptions, RefType};
66use gitbaby::setup::types::{FastImportCommit, ObjectFormat};
77use models::dir::dirs::{self, DirKind};
8use models::repo::repo_collaborations;
89use models::repo::repos;
910use models::space::spaces;
1011use sea_orm::{
11 ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set, TransactionTrait,
12 Unchanged,
12 ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set,
13 TransactionTrait, Unchanged,
1314};
1415use serde::{Deserialize, Serialize};
1516use time::OffsetDateTime;
@@ -23,7 +24,7 @@
2324
2425pub const DEFAULT_BRANCH: &str = "main";
2526
26#[derive(Debug, Clone, Serialize)]
27#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2728pub struct RepoDto {
2829 pub id: Uuid,
2930 pub dir_id: Uuid,
@@ -88,7 +89,7 @@
8889 }
8990}
9091
91#[derive(Debug, Clone, Deserialize)]
92#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
9293pub struct CreateRepoCmd {
9394 pub name: String,
9495 #[serde(default)]
@@ -107,7 +108,7 @@
107108 pub init_message: Option<String>,
108109}
109110
110#[derive(Debug, Clone, Deserialize)]
111#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
111112pub struct UpdateRepoCmd {
112113 #[serde(default)]
113114 pub description: Option<String>,
@@ -168,6 +169,7 @@
168169 .filter(dirs::Column::SpaceId.eq(space.id))
169170 .filter(dirs::Column::ParentDirId.eq(cmd.parent_dir_id))
170171 .order_by_desc(dirs::Column::Position)
172 .lock(sea_orm::sea_query::LockType::Update)
171173 .one(&txn)
172174 .await?;
173175 max_node.map(|n| n.position + 1).unwrap_or(0)
@@ -309,7 +311,11 @@
309311 Ok(RepoDto::from(ctx.model))
310312 }
311313
312 pub async fn list_repos(&self, space_name: String) -> Result<Vec<RepoDto>, GitError> {
314 pub async fn list_repos(
315 &self,
316 space_name: String,
317 caller: Option<Uuid>,
318 ) -> Result<Vec<RepoDto>, GitError> {
313319 let space = self.lookup_space(&space_name).await?;
314320 let dirs = dirs::Entity::find()
315321 .filter(dirs::Column::SpaceId.eq(space.id))
@@ -322,7 +328,25 @@
322328 .order_by_asc(repos::Column::CreatedAt)
323329 .all(self.db_read())
324330 .await?;
325 Ok(rows.into_iter().map(RepoDto::from).collect())
331
332 let owner_view = caller.is_some_and(|uid| space.owner_id == uid);
333 let mut accessible: std::collections::HashSet<Uuid> = std::collections::HashSet::new();
334 if let Some(uid) = caller
335 && !owner_view
336 {
337 let repo_ids: Vec<Uuid> = rows.iter().map(|r| r.id).collect();
338 let collabs = repo_collaborations::Entity::find()
339 .filter(repo_collaborations::Column::UserId.eq(uid))
340 .filter(repo_collaborations::Column::RepoId.is_in(repo_ids))
341 .all(self.db_read())
342 .await?;
343 accessible.extend(collabs.into_iter().map(|c| c.repo_id));
344 }
345 Ok(rows
346 .into_iter()
347 .filter(|r| !r.is_private || owner_view || accessible.contains(&r.id))
348 .map(RepoDto::from)
349 .collect())
326350 }
327351
328352 pub async fn update_repo(
@@ -337,13 +361,11 @@
337361 if space.owner_id != caller {
338362 return Err(GitError::Forbidden);
339363 }
340 let baby = self.baby(&ctx);
341 if let Some(branch) = &cmd.default_branch {
342 let branch = validate_branch_name(branch)?;
343 if branch != ctx.model.default_branch {
344 baby.set_default_branch(&branch).await.map_err(map_baby)?;
345 }
346 }
364 let new_branch = match &cmd.default_branch {
365 Some(b) => Some(validate_branch_name(b)?),
366 None => None,
367 };
368 let old_branch = ctx.model.default_branch.clone();
347369 let now = OffsetDateTime::now_utc();
348370 let mut am = repos::ActiveModel {
349371 id: Unchanged(ctx.model.id),
@@ -361,12 +383,27 @@
361383 if let Some(lfs) = cmd.enable_lfs {
362384 am.enable_lfs = Set(lfs);
363385 }
364 if let Some(branch) = cmd.default_branch {
365 let branch = validate_branch_name(&branch)?;
366 am.default_branch = Set(branch);
386 if let Some(branch) = &new_branch {
387 am.default_branch = Set(branch.clone());
367388 }
368389 am.updated_at = Set(now);
369390 let saved = am.update(self.db_write()).await?;
391
392 if let Some(branch) = new_branch
393 && branch != old_branch
394 {
395 let baby = self.baby(&ctx);
396 if let Err(e) = baby.set_default_branch(&branch).await {
397 let revert = repos::ActiveModel {
398 id: Unchanged(ctx.model.id),
399 default_branch: Set(old_branch),
400 updated_at: Set(now),
401 ..Default::default()
402 };
403 let _ = revert.update(self.db_write()).await;
404 return Err(map_baby(e));
405 }
406 }
370407 Ok(RepoDto::from(saved))
371408 }
372409
@@ -397,23 +434,9 @@
397434 }
398435
399436 async fn repo_space(&self, ctx: &GitContext) -> Result<spaces::Model, GitError> {
400 let mut dir_id = ctx.model.dir_id;
401 loop {
402 let dir = dirs::Entity::find_by_id(dir_id)
403 .one(self.db_read())
404 .await?
405 .ok_or(GitError::NotFound)?;
406 if let Some(space_id) = dir.space_id {
407 return spaces::Entity::find_by_id(space_id)
408 .one(self.db_read())
409 .await?
410 .ok_or(GitError::NotFound);
411 }
412 match dir.parent_dir_id {
413 Some(parent) => dir_id = parent,
414 None => return Err(GitError::NotFound),
415 }
416 }
437 crate::share::access::space_for_dir(self.db_read(), ctx.model.dir_id)
438 .await?
439 .ok_or(GitError::NotFound)
417440 }
418441
419442 pub async fn sync_repo_stats(&self, ctx: &GitContext) -> Result<(), GitError> {
@@ -427,7 +450,11 @@
427450 .filter(|r| r.ref_type == RefType::Branch)
428451 .count() as i32;
429452 let nums_tags = refs.iter().filter(|r| r.ref_type == RefType::Tag).count() as i32;
430 let nums_commits = count_commits(&ctx.repo_dir()).await? as i32;
453 let nums_commits = count_commits(
454 &ctx.repo_dir(),
455 self.state.config.git.git_command_timeout_secs,
456 )
457 .await? as i32;
431458 let git_size = dir_size(&ctx.repo_dir()).await? as i64;
432459
433460 let now = OffsetDateTime::now_utc();
@@ -445,7 +472,7 @@
445472 }
446473}
447474
448pub(crate) async fn count_commits(dir: &Path) -> Result<i64, GitError> {
475pub(crate) async fn count_commits(dir: &Path, timeout: u64) -> Result<i64, GitError> {
449476 let mut cmd = Cmd::new(
450477 "gitdata::git:count_commits",
451478 "git",
@@ -457,7 +484,7 @@
457484 Vec::new(),
458485 dir.to_path_buf(),
459486 Env::new(),
460 None,
487 Some(timeout),
461488 );
462489 let output = cmd
463490 .run()
Modified lib/git/src/service/tags.rs lib/git/src/service/tags.rs +2 −2 View lib/git/src/service/tags.rs
@@ -17,7 +17,7 @@
1717
1818const CALLER: &str = "gitdata::git:tags";
1919
20#[derive(Debug, Clone, Serialize)]
20#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
2121pub struct TagDto {
2222 pub name: String,
2323 pub tag_type: TagType,
@@ -28,7 +28,7 @@
2828 pub created_at: Option<OffsetDateTime>,
2929}
3030
31#[derive(Debug, Clone, Deserialize)]
31#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
3232pub struct CreateTagCmd {
3333 pub name: String,
3434 pub target: String,
Modified lib/git/src/lib.rs lib/git/src/lib.rs +1 −0 View lib/git/src/lib.rs
@@ -3,6 +3,7 @@
33pub mod graphql;
44pub mod handler;
55pub mod lfs;
6pub mod openapi;
67pub mod routes;
78pub mod service;
89pub mod share;
Added lib/git/src/openapi.rs lib/git/src/openapi.rs +191 −0 View lib/git/src/openapi.rs
@@ -0,0 +1,191 @@
1use utoipa::OpenApi;
2
3use crate::handler;
4
5#[derive(OpenApi)]
6#[openapi(
7 info(title = "gitdata git api", version = "1.0"),
8 paths(
9 handler::graphql::graphql_get,
10 handler::graphql::graphql_post,
11 handler::analysis::code_activity::code_activity,
12 handler::analysis::contributors::contributors,
13 handler::analysis::languages::languages,
14 handler::analysis::languages_read::languages_read,
15 handler::archives::create::create,
16 handler::archives::create_bundle::create_bundle,
17 handler::archives::download::download,
18 handler::blame::blame,
19 handler::blobs::content::blob_content,
20 handler::blobs::list_lfs_pointers::list_lfs_pointers,
21 handler::blobs::metadata::blob_metadata,
22 handler::blobs::raw_download::blob_raw_download,
23 handler::branches::create::create_branch,
24 handler::branches::delete::delete_branch,
25 handler::branches::get::get_branch,
26 handler::branches::list::list_branches,
27 handler::branches::rename::rename_branch,
28 handler::branches::set_default::set_default_branch,
29 handler::commits::get::get_commit,
30 handler::commits::is_ancestor::is_ancestor,
31 handler::commits::list::list_commits,
32 handler::commits::list_commit_statuses::list_commit_statuses,
33 handler::commits::rev_parse::rev_parse,
34 handler::commits::set_commit_status::set_commit_status,
35 handler::commits::stats::commit_stats,
36 handler::commits::sync_commit::sync_commit,
37 handler::commits::tree_entries::get_tree_entries,
38 handler::conflicts::check::conflict_check,
39 handler::conflicts::check_threeway::conflict_check_threeway,
40 handler::conflicts::conflicted_files::conflicted_files,
41 handler::conflicts::detail::conflict_detail,
42 handler::content::caller_signature::caller_signature,
43 handler::content::commit_object::commit_object,
44 handler::content::write_blob::write_blob,
45 handler::content::write_tree_from_files::write_tree_from_files,
46 handler::diffs::files::diff_files,
47 handler::diffs::files_changed_between::files_changed_between,
48 handler::diffs::for_commit::diff_for_commit,
49 handler::diffs::range_diff::range_diff,
50 handler::diffs::raw::diff_raw,
51 handler::diffs::short_stat::diff_short_stat,
52 handler::diffs::stats::diff_stats,
53 handler::diffs::tree::diff_tree,
54 handler::hooks::execute_script::execute_script,
55 handler::lfs::cleanup_unreferenced::cleanup_unreferenced,
56 handler::lfs::list_locks::list_locks,
57 handler::lfs::lock::lock,
58 handler::lfs::unlock::unlock,
59 handler::lfs::unlock_by_id::unlock_by_id,
60 handler::lfs::verify_locks::verify_locks,
61 handler::logs::commits_between::log_commits_between,
62 handler::logs::graph::log_graph,
63 handler::logs::last_commit::log_last_commit,
64 handler::logs::list::log_list,
65 handler::maintenance::fsck::fsck,
66 handler::maintenance::prune::prune,
67 handler::packs::ingest::ingest,
68 handler::packs::repack::repack,
69 handler::packs::stats::stats,
70 handler::patches::apply::patch_apply,
71 handler::patches::generate::patch_generate,
72 handler::patches::parse::patch_parse,
73 handler::path_log::path_log,
74 handler::pipelines::cancel::cancel,
75 handler::pipelines::schedule::schedule,
76 handler::pipelines::trigger::trigger,
77 handler::refs::branches::refs_branches,
78 handler::refs::delete::refs_delete,
79 handler::refs::filtered::refs_list_filtered,
80 handler::refs::get::refs_get,
81 handler::refs::head::refs_head,
82 handler::refs::is_object_in_ref::refs_is_object_in_ref,
83 handler::refs::list::refs_list,
84 handler::refs::resolve::refs_resolve,
85 handler::refs::update::refs_update,
86 handler::remotes::add::add,
87 handler::remotes::clone_import::clone_import,
88 handler::remotes::fetch::fetch,
89 handler::remotes::push::push,
90 handler::remotes::remove::remove,
91 handler::repos::archive::archive_repo,
92 handler::repos::create::create_repo,
93 handler::repos::get::get_repo,
94 handler::repos::list::list_repos,
95 handler::repos::update::update_repo,
96 handler::signatures::verify_commit::verify_commit,
97 handler::signatures::verify_tag::verify_tag,
98 handler::submodules::list::submodule_list,
99 handler::submodules::resolve::submodule_resolve,
100 handler::tags::create::create_tag,
101 handler::tags::delete::delete_tag,
102 handler::tags::get::get_tag,
103 handler::tags::list::list_tags,
104 handler::tags::verify::verify_tag,
105 handler::trees::follow::tree_follow,
106 handler::trees::last_updated::tree_last_updated,
107 handler::trees::read::tree_read,
108 handler::trees::resolve_path::tree_resolve_path,
109 handler::trees::walk::tree_walk,
110 ),
111 components(schemas(
112 export::rest::NoData,
113 models::git::git_commit_status::CommitStatusState,
114 models::git::git_tags::TagType,
115 crate::lfs::types::LfsLock,
116 crate::lfs::types::LfsLockOwner,
117 crate::lfs::types::LfsLockListResponse,
118 crate::lfs::types::LfsVerifyLocksResponse,
119 crate::service::analysis::CodeActivityAuthorDto,
120 crate::service::analysis::CodeActivityDto,
121 crate::service::analysis::ContributorStatDto,
122 crate::service::analysis::LanguageStatDto,
123 crate::service::archives::ArchiveResultDto,
124 crate::service::blame::BlameRangeDto,
125 crate::service::blobs::BlobContentDto,
126 crate::service::blobs::BlobMetaDto,
127 crate::service::blobs::LfsPointerDto,
128 crate::service::blobs::RawDownloadDto,
129 crate::service::branches::BranchDto,
130 crate::service::branches::CreateBranchCmd,
131 crate::service::commits::CommitStatusDto,
132 crate::service::commits::CommitStatusDtoState,
133 crate::service::commits::SetCommitStatusCmd,
134 crate::service::conflicts::ConflictDetailDto,
135 crate::service::conflicts::MergeStatusDto,
136 crate::service::content::FileEntryDto,
137 crate::service::content::ObjectKind,
138 crate::service::content::SignatureDto,
139 crate::service::content::WriteObjectResultDto,
140 crate::service::diffs::DiffTreeRecordDto,
141 crate::service::hooks::ScriptOutputDto,
142 crate::service::maintenance::FsckDto,
143 crate::service::maintenance::PruneResultDto,
144 crate::service::packs::IngestPackDto,
145 crate::service::packs::PackStatsDto,
146 crate::service::patches::AppliedPatchDto,
147 crate::service::patches::ParsedPatchDto,
148 crate::service::patches::PatchDto,
149 crate::service::path_log::PathLogEntryDto,
150 crate::service::pipelines::CommitStatusState,
151 crate::service::pipelines::JobDto,
152 crate::service::pipelines::TriggerResultDto,
153 crate::service::refs::BranchInfoDto,
154 crate::service::refs::HeadInfoDto,
155 crate::service::refs::UpdateRefResultDto,
156 crate::service::remotes::FetchResultDto,
157 crate::service::remotes::ImportResultDto,
158 crate::service::remotes::PushResultDto,
159 crate::service::remotes::RemoteAuthDto,
160 crate::service::repos::CreateRepoCmd,
161 crate::service::repos::RepoDto,
162 crate::service::repos::UpdateRepoCmd,
163 crate::service::signatures::VerifyResultDto,
164 crate::service::submodules::SubmoduleDto,
165 crate::service::tags::CreateTagCmd,
166 crate::service::tags::TagDto,
167 crate::service::trees::PathLastUpdated,
168 crate::handler::commits::is_ancestor::IsAncestorResponse,
169 crate::handler::commits::sync_commit::CommitSyncResponse,
170 crate::handler::conflicts::check_threeway::ConflictCheckThreewayBody,
171 crate::handler::content::caller_signature::CallerSignatureBody,
172 crate::handler::content::commit_object::CommitObjectBody,
173 crate::handler::content::write_blob::WriteBlobBody,
174 crate::handler::content::write_tree_from_files::WriteTreeBody,
175 crate::handler::hooks::execute_script::HookExecBody,
176 crate::handler::lfs::cleanup_unreferenced::LfsCleanupResponse,
177 crate::handler::lfs::lock::LfsLockBody,
178 crate::handler::lfs::unlock::LfsUnlockBody,
179 crate::handler::lfs::unlock_by_id::LfsUnlockByIdBody,
180 crate::handler::patches::apply::PatchApplyBody,
181 crate::handler::pipelines::cancel::PipelineCancelBody,
182 crate::handler::pipelines::schedule::PipelineScheduleBody,
183 crate::handler::refs::is_object_in_ref::IsObjectInRefResponse,
184 crate::handler::remotes::add::RemoteAddBody,
185 crate::handler::remotes::clone_import::RemoteImportBody,
186 crate::handler::remotes::fetch::RemoteFetchBody,
187 crate::handler::remotes::push::RemotePushBody,
188 crate::handler::tags::verify::TagVerifyDto,
189 ))
190)]
191pub struct GitApiDoc;
No newline at end of file
Modified lib/models/src/git/git_commit_status.rs lib/models/src/git/git_commit_status.rs +1 −0 View lib/models/src/git/git_commit_status.rs
@@ -5,6 +5,7 @@
55
66#[derive(
77 Debug, Clone, Copy, PartialEq, Eq, Default, EnumIter, DeriveActiveEnum, Serialize, Deserialize,
8 utoipa::ToSchema,
89)]
910#[sea_orm(rs_type = "i16", db_type = "SmallInteger")]
1011pub enum CommitStatusState {
Modified lib/models/src/git/git_tags.rs lib/models/src/git/git_tags.rs +1 −0 View lib/models/src/git/git_tags.rs
@@ -5,6 +5,7 @@
55
66#[derive(
77 Debug, Clone, Copy, PartialEq, Eq, Default, EnumIter, DeriveActiveEnum, Serialize, Deserialize,
8 utoipa::ToSchema,
89)]
910#[sea_orm(rs_type = "i16", db_type = "SmallInteger")]
1011pub enum TagType {