feat(server): add tracing spans and caching to archive and blame services
- Add tracing spans with repo labels for archive and blame operations - Implement caching for archive list entries when using OID selectors - Implement caching for blame operations when using OID selectors - Add detailed
This commit is contained in:
+18
-4
@@ -1,6 +1,6 @@
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status, into_stream};
|
||||
use super::{GitksService, cache, into_status};
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl archive_service_server::ArchiveService for GitksService {
|
||||
@@ -12,9 +12,13 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
request: tonic::Request<ArchiveRequest>,
|
||||
) -> Result<tonic::Response<Self::GetArchiveStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("archive.get_archive", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let chunks = gb.get_archive(inner).map_err(into_status)?;
|
||||
Ok(tonic::Response::new(into_stream(chunks)))
|
||||
let stream = gb.get_archive_stream(inner)?;
|
||||
tracing::info!(%repo, "archive streaming started");
|
||||
Ok(tonic::Response::new(stream))
|
||||
}
|
||||
|
||||
async fn list_archive_entries(
|
||||
@@ -22,8 +26,18 @@ impl archive_service_server::ArchiveService for GitksService {
|
||||
request: tonic::Request<ListArchiveEntriesRequest>,
|
||||
) -> Result<tonic::Response<ListArchiveEntriesResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("archive.list_archive_entries", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_archive_entries(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.treeish) {
|
||||
cache::cached_response("archive.list_archive_entries", &inner, || {
|
||||
gb.list_archive_entries(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.list_archive_entries(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.entries.len(), "list_archive_entries done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+24
-3
@@ -1,6 +1,6 @@
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status, into_stream};
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl blame_service_server::BlameService for GitksService {
|
||||
@@ -12,8 +12,19 @@ impl blame_service_server::BlameService for GitksService {
|
||||
request: tonic::Request<BlameRequest>,
|
||||
) -> Result<tonic::Response<BlameResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("blame.blame", %repo, %path);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.blame(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("blame.blame", &inner, || {
|
||||
gb.blame(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.blame(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, %path, hunks = resp.hunks.len(), "blame done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -22,8 +33,18 @@ impl blame_service_server::BlameService for GitksService {
|
||||
request: tonic::Request<BlameRequest>,
|
||||
) -> Result<tonic::Response<Self::StreamBlameStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("blame.stream_blame", %repo, %path);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.blame(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("blame.blame", &inner, || {
|
||||
gb.blame(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.blame(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(into_stream(resp.hunks)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<ListBranchesRequest>,
|
||||
) -> Result<tonic::Response<ListBranchesResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("branch.list_branches", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_branches(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.branches.len(), "list_branches done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -19,6 +23,10 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<GetBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.get_branch", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_branch(inner).map_err(into_status)?;
|
||||
Ok(tonic::Response::new(resp))
|
||||
@@ -29,8 +37,13 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<CreateBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.create_branch", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.create_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch created");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -39,8 +52,13 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<DeleteBranchRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.delete_branch", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
gb.delete_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch deleted");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -49,8 +67,14 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<RenameBranchRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let old = inner.old_name.clone();
|
||||
let new = inner.new_name.clone();
|
||||
let span = tracing::info_span!("branch.rename_branch", %repo, %old, %new);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.rename_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, old = %old, new = %new, "branch renamed");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -59,8 +83,13 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<UpdateBranchTargetRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.update_branch_target", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.update_branch_target(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch target updated");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -69,8 +98,13 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<SetBranchUpstreamRequest>,
|
||||
) -> Result<tonic::Response<Branch>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("branch.set_branch_upstream", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.set_branch_upstream(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "branch upstream set");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -79,8 +113,14 @@ impl branch_service_server::BranchService for GitksService {
|
||||
request: tonic::Request<CompareBranchRequest>,
|
||||
) -> Result<tonic::Response<CompareBranchResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let source = inner.source_branch.clone();
|
||||
let target = inner.target_branch.clone();
|
||||
let span = tracing::info_span!("branch.compare_branch", %repo, %source, %target);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.compare_branch(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %source, %target, ahead = resp.ahead_by, behind = resp.behind_by, "branch compared");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+62
-5
@@ -1,6 +1,6 @@
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status};
|
||||
use super::{GitksService, cache, into_status};
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl commit_service_server::CommitService for GitksService {
|
||||
@@ -9,8 +9,18 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<ListCommitsRequest>,
|
||||
) -> Result<tonic::Response<ListCommitsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.list_commits", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_commits(inner).map_err(into_status)?;
|
||||
let resp = if !inner.all && cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.list_commits", &inner, || {
|
||||
gb.list_commits(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.list_commits(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), total = resp.page_info.as_ref().map(|p| p.total_count).unwrap_or(0), "list_commits done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -19,8 +29,17 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<GetCommitRequest>,
|
||||
) -> Result<tonic::Response<Commit>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.get_commit", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_commit(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.get_commit", &inner, || {
|
||||
gb.get_commit(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_commit(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -29,8 +48,18 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<GetCommitAncestorsRequest>,
|
||||
) -> Result<tonic::Response<GetCommitAncestorsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.get_commit_ancestors", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_commit_ancestors(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("commit.get_commit_ancestors", &inner, || {
|
||||
gb.get_commit_ancestors(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_commit_ancestors(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), "get_commit_ancestors done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -39,8 +68,16 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<CreateCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.create_commit", %repo, %branch);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.create_commit(inner).map_err(into_status)?;
|
||||
let commit_hex = resp.commit.as_ref()
|
||||
.and_then(|c| c.oid.as_ref().map(|o| o.hex.as_str()).or(Some("?")))
|
||||
.unwrap_or("?");
|
||||
tracing::info!(%repo, %branch, %commit_hex, "commit created");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -49,8 +86,13 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<RevertCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.revert_commit", %repo, %branch);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.revert_commit(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, "commit reverted");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -59,8 +101,13 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<CherryPickCommitRequest>,
|
||||
) -> Result<tonic::Response<CreateCommitResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("commit.cherry_pick_commit", %repo, %branch);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.cherry_pick_commit(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, "commit cherry-picked");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -69,8 +116,18 @@ impl commit_service_server::CommitService for GitksService {
|
||||
request: tonic::Request<CompareCommitsRequest>,
|
||||
) -> Result<tonic::Response<CompareCommitsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("commit.compare_commits", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.compare_commits(inner).map_err(into_status)?;
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("commit.compare_commits", &inner, || {
|
||||
gb.compare_commits(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.compare_commits(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.commits.len(), "compare_commits done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+43
-5
@@ -1,6 +1,6 @@
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status, into_stream};
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl diff_service_server::DiffService for GitksService {
|
||||
@@ -12,8 +12,18 @@ impl diff_service_server::DiffService for GitksService {
|
||||
request: tonic::Request<GetDiffRequest>,
|
||||
) -> Result<tonic::Response<GetDiffResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_diff", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_diff(inner).map_err(into_status)?;
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("diff.get_diff", &inner, || {
|
||||
gb.get_diff(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_diff(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, files = resp.files.len(), overflow = resp.overflow, "get_diff done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -22,8 +32,18 @@ impl diff_service_server::DiffService for GitksService {
|
||||
request: tonic::Request<GetCommitDiffRequest>,
|
||||
) -> Result<tonic::Response<GetDiffResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_commit_diff", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_commit_diff(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.commit) {
|
||||
cache::cached_response("diff.get_commit_diff", &inner, || {
|
||||
gb.get_commit_diff(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_commit_diff(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, files = resp.files.len(), "get_commit_diff done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -32,8 +52,17 @@ impl diff_service_server::DiffService for GitksService {
|
||||
request: tonic::Request<GetPatchRequest>,
|
||||
) -> Result<tonic::Response<Self::GetPatchStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_patch", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let items = gb.get_patch(inner).map_err(into_status)?;
|
||||
let items = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_vec_response("diff.get_patch", &inner, || {
|
||||
gb.get_patch(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_patch(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(into_stream(items)))
|
||||
}
|
||||
|
||||
@@ -42,8 +71,17 @@ impl diff_service_server::DiffService for GitksService {
|
||||
request: tonic::Request<GetDiffStatsRequest>,
|
||||
) -> Result<tonic::Response<DiffStats>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("diff.get_diff_stats", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_diff_stats(inner).map_err(into_status)?;
|
||||
let resp = if cache::selectors_are_oid(&inner.base, &inner.head) {
|
||||
cache::cached_response("diff.get_diff_stats", &inner, || {
|
||||
gb.get_diff_stats(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_diff_stats(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ impl merge_service_server::MergeService for GitksService {
|
||||
request: tonic::Request<CheckMergeRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("merge.check_merge", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.check_merge(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, status = resp.status, "check_merge done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -19,8 +23,13 @@ impl merge_service_server::MergeService for GitksService {
|
||||
request: tonic::Request<MergeRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let target = inner.target_branch.clone();
|
||||
let span = tracing::info_span!("merge.merge", %repo, %target);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.merge(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %target, status = resp.status, "merge done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -29,8 +38,12 @@ impl merge_service_server::MergeService for GitksService {
|
||||
request: tonic::Request<ListMergeConflictsRequest>,
|
||||
) -> Result<tonic::Response<ListMergeConflictsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("merge.list_merge_conflicts", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_merge_conflicts(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, conflicts = resp.conflicts.len(), "list_merge_conflicts done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -39,8 +52,13 @@ impl merge_service_server::MergeService for GitksService {
|
||||
request: tonic::Request<ResolveMergeConflictsRequest>,
|
||||
) -> Result<tonic::Response<MergeResult>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let target = inner.target_branch.clone();
|
||||
let span = tracing::info_span!("merge.resolve_merge_conflicts", %repo, %target);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.resolve_merge_conflicts(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %target, status = resp.status, "merge conflicts resolved");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -49,8 +67,13 @@ impl merge_service_server::MergeService for GitksService {
|
||||
request: tonic::Request<RebaseRequest>,
|
||||
) -> Result<tonic::Response<RebaseResult>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let branch = inner.branch.clone();
|
||||
let span = tracing::info_span!("merge.rebase", %repo, %branch);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.rebase(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %branch, status = resp.status, "rebase done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+61
-17
@@ -1,6 +1,7 @@
|
||||
mod archive;
|
||||
mod blame;
|
||||
mod branch;
|
||||
mod cache;
|
||||
mod commit;
|
||||
mod diff;
|
||||
mod merge;
|
||||
@@ -23,11 +24,23 @@ use crate::pb::{
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GitksService {
|
||||
/// 所有仓库的根路径前缀
|
||||
pub(crate) repo_prefix: PathBuf,
|
||||
/// Root prefix path for all repositories
|
||||
pub repo_prefix: PathBuf,
|
||||
}
|
||||
|
||||
impl GitksService {
|
||||
fn repo_label(&self, header: Option<&crate::pb::RepositoryHeader>) -> String {
|
||||
header
|
||||
.and_then(|h| {
|
||||
if h.relative_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(h.relative_path.clone())
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".into())
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(
|
||||
&self,
|
||||
header: Option<&crate::pb::RepositoryHeader>,
|
||||
@@ -35,7 +48,12 @@ impl GitksService {
|
||||
let header =
|
||||
header.ok_or_else(|| tonic::Status::invalid_argument("repository is required"))?;
|
||||
let header = self.prefixed_header(header);
|
||||
GitBare::from_repository_header(&header).map_err(into_status)
|
||||
let gb = GitBare::from_repository_header(&header).map_err(into_status)?;
|
||||
tracing::debug!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
"resolved repository"
|
||||
);
|
||||
Ok(gb)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_for_init(
|
||||
@@ -49,7 +67,7 @@ impl GitksService {
|
||||
return Err(tonic::Status::invalid_argument("relative_path is required"));
|
||||
}
|
||||
let candidate = self.repo_prefix.join(relative_path);
|
||||
// 路径穿越检查
|
||||
// Path traversal check
|
||||
let canonical = candidate
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|_| candidate.clone());
|
||||
@@ -65,11 +83,8 @@ impl GitksService {
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
/// 将客户端传入的 header 注入 repo_prefix 作为 storage_path
|
||||
fn prefixed_header(
|
||||
&self,
|
||||
header: &crate::pb::RepositoryHeader,
|
||||
) -> crate::pb::RepositoryHeader {
|
||||
/// Inject repo_prefix as storage_path into the client-provided header
|
||||
fn prefixed_header(&self, header: &crate::pb::RepositoryHeader) -> crate::pb::RepositoryHeader {
|
||||
crate::pb::RepositoryHeader {
|
||||
storage_path: self.repo_prefix.to_string_lossy().into_owned(),
|
||||
relative_path: header.relative_path.clone(),
|
||||
@@ -115,20 +130,49 @@ pub(crate) fn git_cmd(gb: &GitBare, args: &[&str]) -> Result<std::process::Outpu
|
||||
gb.bare_dir.to_string_lossy().into_owned(),
|
||||
];
|
||||
full_args.extend(args.iter().map(|s| s.to_string()));
|
||||
std::process::Command::new("git")
|
||||
tracing::debug!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
args = %full_args.iter().skip(2).cloned().collect::<Vec<_>>().join(" "),
|
||||
"spawning git subprocess"
|
||||
);
|
||||
let result = std::process::Command::new("git")
|
||||
.args(&full_args)
|
||||
.output()
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))
|
||||
.map_err(|e| {
|
||||
tracing::error!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
error = %e,
|
||||
"failed to spawn git subprocess"
|
||||
);
|
||||
tonic::Status::internal(e.to_string())
|
||||
})?;
|
||||
if !result.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&result.stderr);
|
||||
tracing::warn!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
status = ?result.status.code(),
|
||||
stderr = %stderr.trim(),
|
||||
"git subprocess exited with non-zero status"
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn serve(
|
||||
addr: std::net::SocketAddr,
|
||||
repo_prefix: PathBuf,
|
||||
) -> Result<(), tonic::transport::Error> {
|
||||
let span = tracing::info_span!("gitks.server", %addr);
|
||||
let _enter = span.enter();
|
||||
let svc = GitksService { repo_prefix };
|
||||
tonic::transport::Server::builder()
|
||||
.add_service(repository_service_server::RepositoryServiceServer::new(svc.clone()))
|
||||
.add_service(archive_service_server::ArchiveServiceServer::new(svc.clone()))
|
||||
tracing::info!("registering gRPC services");
|
||||
let server = tonic::transport::Server::builder()
|
||||
.add_service(repository_service_server::RepositoryServiceServer::new(
|
||||
svc.clone(),
|
||||
))
|
||||
.add_service(archive_service_server::ArchiveServiceServer::new(
|
||||
svc.clone(),
|
||||
))
|
||||
.add_service(blame_service_server::BlameServiceServer::new(svc.clone()))
|
||||
.add_service(branch_service_server::BranchServiceServer::new(svc.clone()))
|
||||
.add_service(commit_service_server::CommitServiceServer::new(svc.clone()))
|
||||
@@ -136,7 +180,7 @@ pub async fn serve(
|
||||
.add_service(merge_service_server::MergeServiceServer::new(svc.clone()))
|
||||
.add_service(pack_service_server::PackServiceServer::new(svc.clone()))
|
||||
.add_service(tag_service_server::TagServiceServer::new(svc.clone()))
|
||||
.add_service(tree_service_server::TreeServiceServer::new(svc))
|
||||
.serve(addr)
|
||||
.await
|
||||
.add_service(tree_service_server::TreeServiceServer::new(svc));
|
||||
tracing::info!("server ready, starting to accept connections");
|
||||
server.serve(addr).await
|
||||
}
|
||||
|
||||
@@ -16,8 +16,12 @@ impl pack_service_server::PackService for GitksService {
|
||||
request: tonic::Request<AdvertiseRefsRequest>,
|
||||
) -> Result<tonic::Response<AdvertiseRefsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.advertise_refs", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.advertise_refs(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, refs = resp.references.len(), "advertise_refs done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -30,6 +34,10 @@ impl pack_service_server::PackService for GitksService {
|
||||
.next()
|
||||
.await
|
||||
.ok_or_else(|| tonic::Status::invalid_argument("empty upload-pack stream"))??;
|
||||
let repo = self.repo_label(first.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.upload_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
tracing::info!(%repo, "upload-pack streaming started");
|
||||
let gb = self.resolve(first.repository.as_ref())?;
|
||||
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
@@ -57,6 +65,10 @@ impl pack_service_server::PackService for GitksService {
|
||||
.next()
|
||||
.await
|
||||
.ok_or_else(|| tonic::Status::invalid_argument("empty receive-pack stream"))??;
|
||||
let repo = self.repo_label(first.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.receive_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
tracing::info!(%repo, "receive-pack streaming started");
|
||||
let gb = self.resolve(first.repository.as_ref())?;
|
||||
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(16);
|
||||
@@ -80,8 +92,12 @@ impl pack_service_server::PackService for GitksService {
|
||||
request: tonic::Request<PackObjectsRequest>,
|
||||
) -> Result<tonic::Response<Self::PackObjectsStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.pack_objects", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let stream = gb.pack_objects(inner).await?;
|
||||
tracing::info!(%repo, "pack-objects streaming started");
|
||||
Ok(tonic::Response::new(stream))
|
||||
}
|
||||
|
||||
@@ -94,8 +110,12 @@ impl pack_service_server::PackService for GitksService {
|
||||
while let Some(msg) = stream.next().await {
|
||||
inputs.push(msg?);
|
||||
}
|
||||
let repo = self.repo_label(inputs.first().and_then(|r| r.repository.as_ref()));
|
||||
let span = tracing::info_span!("pack.index_pack", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inputs.first().and_then(|r| r.repository.as_ref()))?;
|
||||
let resp = gb.index_pack(inputs).map_err(into_status)?;
|
||||
tracing::info!(%repo, objects = resp.object_count, "index_pack done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -104,8 +124,12 @@ impl pack_service_server::PackService for GitksService {
|
||||
request: tonic::Request<ListPackfilesRequest>,
|
||||
) -> Result<tonic::Response<ListPackfilesResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.list_packfiles", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_packfiles(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.packfiles.len(), "list_packfiles done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -114,8 +138,12 @@ impl pack_service_server::PackService for GitksService {
|
||||
request: tonic::Request<FsckRequest>,
|
||||
) -> Result<tonic::Response<FsckResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("pack.fsck", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.fsck(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, ok = resp.ok, errors = resp.errors.len(), warnings = resp.warnings.len(), "fsck done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+61
-15
@@ -21,6 +21,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<GetRepositoryRequest>,
|
||||
) -> Result<tonic::Response<Repository>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let bare = gb.bare_dir.join("HEAD").exists();
|
||||
let object_format = gb.object_format();
|
||||
@@ -38,9 +41,13 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<InitRepositoryRequest>,
|
||||
) -> Result<tonic::Response<Repository>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.init_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
let bare_dir = self.resolve_for_init(inner.repository.as_ref())?;
|
||||
let gb = crate::bare::GitBare { bare_dir };
|
||||
let gb = crate::bare::GitBare::new(bare_dir);
|
||||
gb.init_repository(inner.bare).map_err(into_status)?;
|
||||
tracing::info!(%repo, bare = inner.bare, "repository initialized");
|
||||
Ok(tonic::Response::new(Repository {
|
||||
header: inner.repository,
|
||||
bare: inner.bare,
|
||||
@@ -53,8 +60,13 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<DeleteRepositoryRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.delete_repository", %repo);
|
||||
let _enter = span.enter();
|
||||
let bare_dir = self.resolve_for_init(inner.repository.as_ref())?;
|
||||
tracing::warn!(%repo, path = %bare_dir.display(), "deleting repository");
|
||||
std::fs::remove_dir_all(&bare_dir).map_err(|e| tonic::Status::internal(e.to_string()))?;
|
||||
tracing::info!(%repo, "repository deleted");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -63,6 +75,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<RepositoryExistsRequest>,
|
||||
) -> Result<tonic::Response<RepositoryExistsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.repository_exists", %repo);
|
||||
let _enter = span.enter();
|
||||
let bare_dir = self.resolve_for_init(inner.repository.as_ref())?;
|
||||
let exists = bare_dir.exists() && bare_dir.is_dir() && bare_dir.join("HEAD").exists();
|
||||
Ok(tonic::Response::new(RepositoryExistsResponse { exists }))
|
||||
@@ -73,6 +88,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<RepositoryObjectFormatRequest>,
|
||||
) -> Result<tonic::Response<RepositoryObjectFormatResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_object_format", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(RepositoryObjectFormatResponse {
|
||||
object_format: gb.object_format() as i32,
|
||||
@@ -84,6 +102,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<GetDefaultBranchRequest>,
|
||||
) -> Result<tonic::Response<GetDefaultBranchResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_default_branch", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(GetDefaultBranchResponse {
|
||||
name: default_branch_name(&gb),
|
||||
@@ -95,6 +116,10 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<SetDefaultBranchRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("repo.set_default_branch", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let refname = format!("refs/heads/{}", inner.name);
|
||||
let out = git_cmd(&gb, &["symbolic-ref", "HEAD", &refname])?;
|
||||
@@ -103,6 +128,7 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
String::from_utf8_lossy(&out.stderr).trim().to_string(),
|
||||
));
|
||||
}
|
||||
tracing::info!(%repo, %name, "default branch set");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -111,6 +137,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<GetRepositoryConfigRequest>,
|
||||
) -> Result<tonic::Response<GetRepositoryConfigResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_repository_config", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let mut entries = Vec::new();
|
||||
if inner.keys.is_empty() {
|
||||
@@ -156,6 +185,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<SetRepositoryConfigRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.set_repository_config", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
for entry in &inner.entries {
|
||||
if entry.values.is_empty() {
|
||||
@@ -178,6 +210,9 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<RepositoryStatisticsRequest>,
|
||||
) -> Result<tonic::Response<RepositoryStatistics>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.get_repository_statistics", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(repository_maint::get_statistics(&gb)))
|
||||
}
|
||||
@@ -187,11 +222,13 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<RepositoryHealthRequest>,
|
||||
) -> Result<tonic::Response<RepositoryHealthResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.check_repository_health", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(repository_maint::check_health(
|
||||
&gb,
|
||||
inner.connectivity_only,
|
||||
)?))
|
||||
let resp = repository_maint::check_health(&gb, inner.connectivity_only)?;
|
||||
tracing::info!(%repo, ok = resp.ok, errors = resp.errors.len(), warnings = resp.warnings.len(), "health check done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
async fn garbage_collect(
|
||||
@@ -199,12 +236,13 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<GarbageCollectRequest>,
|
||||
) -> Result<tonic::Response<RepositoryMaintenanceResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.garbage_collect", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(repository_maint::run_gc(
|
||||
&gb,
|
||||
inner.prune,
|
||||
inner.aggressive,
|
||||
)?))
|
||||
let resp = repository_maint::run_gc(&gb, inner.prune, inner.aggressive)?;
|
||||
tracing::info!(%repo, ok = resp.ok, "gc done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
async fn repack(
|
||||
@@ -212,13 +250,18 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<RepackRequest>,
|
||||
) -> Result<tonic::Response<RepositoryMaintenanceResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.repack", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(repository_maint::run_repack(
|
||||
let resp = repository_maint::run_repack(
|
||||
&gb,
|
||||
inner.full,
|
||||
inner.write_bitmaps,
|
||||
inner.write_multi_pack_index,
|
||||
)?))
|
||||
)?;
|
||||
tracing::info!(%repo, ok = resp.ok, "repack done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
async fn write_commit_graph(
|
||||
@@ -226,9 +269,12 @@ impl repository_service_server::RepositoryService for GitksService {
|
||||
request: tonic::Request<WriteCommitGraphRequest>,
|
||||
) -> Result<tonic::Response<RepositoryMaintenanceResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("repo.write_commit_graph", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
Ok(tonic::Response::new(
|
||||
repository_maint::run_commit_graph_write(&gb, inner.split, inner.replace)?,
|
||||
))
|
||||
let resp = repository_maint::run_commit_graph_write(&gb, inner.split, inner.replace)?;
|
||||
tracing::info!(%repo, ok = resp.ok, "commit-graph write done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+49
-12
@@ -10,19 +10,32 @@ pub(crate) fn maintenance_response(out: std::process::Output) -> RepositoryMaint
|
||||
}
|
||||
}
|
||||
|
||||
fn dir_size(path: &std::path::Path) -> u64 {
|
||||
let mut total = 0u64;
|
||||
if let Ok(entries) = std::fs::read_dir(path) {
|
||||
for entry in entries.flatten() {
|
||||
let p = entry.path();
|
||||
if p.is_file() {
|
||||
total += entry.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
} else if p.is_dir() {
|
||||
total += dir_size(&p);
|
||||
}
|
||||
/// Get approximate repository size using git count-objects instead of
|
||||
/// recursively scanning the filesystem (which is O(n) and very slow for large repos).
|
||||
fn dir_size(gb: &crate::bare::GitBare) -> u64 {
|
||||
let out = git_cmd(gb, &["count-objects", "-v"]).ok();
|
||||
let text = out
|
||||
.as_ref()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut loose_size_kb = 0u64;
|
||||
let mut pack_size_kb = 0u64;
|
||||
let mut garbage_size_kb = 0u64;
|
||||
|
||||
for line in text.lines() {
|
||||
let line = line.trim();
|
||||
if let Some(val) = line.strip_prefix("size: ") {
|
||||
loose_size_kb = val.trim().parse().unwrap_or(0);
|
||||
} else if let Some(val) = line.strip_prefix("size-pack: ") {
|
||||
pack_size_kb = val.trim().parse().unwrap_or(0);
|
||||
} else if let Some(val) = line.strip_prefix("size-garbage: ") {
|
||||
garbage_size_kb = val.trim().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
total
|
||||
|
||||
// count-objects reports sizes in KiB; convert to bytes
|
||||
(loose_size_kb + pack_size_kb + garbage_size_kb) * 1024
|
||||
}
|
||||
|
||||
fn count_refs(gb: &crate::bare::GitBare) -> u64 {
|
||||
@@ -44,7 +57,7 @@ fn file_len(path: &std::path::Path) -> u64 {
|
||||
}
|
||||
|
||||
pub(crate) fn get_statistics(gb: &crate::bare::GitBare) -> RepositoryStatistics {
|
||||
let size_bytes = dir_size(&gb.bare_dir);
|
||||
let size_bytes = dir_size(gb);
|
||||
|
||||
let mut loose_object_count: u64 = 0;
|
||||
let mut packed_object_count: u64 = 0;
|
||||
@@ -81,6 +94,11 @@ pub(crate) fn check_health(
|
||||
gb: &crate::bare::GitBare,
|
||||
connectivity_only: bool,
|
||||
) -> Result<RepositoryHealthResponse, tonic::Status> {
|
||||
tracing::info!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
connectivity_only = connectivity_only,
|
||||
"running health check"
|
||||
);
|
||||
let mut args: Vec<&str> = vec!["fsck"];
|
||||
if connectivity_only {
|
||||
args.push("--connectivity-only");
|
||||
@@ -109,6 +127,12 @@ pub(crate) fn run_gc(
|
||||
prune: bool,
|
||||
aggressive: bool,
|
||||
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
||||
tracing::info!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
prune = prune,
|
||||
aggressive = aggressive,
|
||||
"running garbage collection"
|
||||
);
|
||||
let mut args: Vec<&str> = vec!["gc"];
|
||||
if prune {
|
||||
args.push("--prune=now");
|
||||
@@ -126,6 +150,13 @@ pub(crate) fn run_repack(
|
||||
write_bitmaps: bool,
|
||||
write_multi_pack_index: bool,
|
||||
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
||||
tracing::info!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
full = full,
|
||||
write_bitmaps = write_bitmaps,
|
||||
write_multi_pack_index = write_multi_pack_index,
|
||||
"running repack"
|
||||
);
|
||||
let mut args: Vec<&str> = vec!["repack", "-d"];
|
||||
if full {
|
||||
args.push("-a");
|
||||
@@ -145,6 +176,12 @@ pub(crate) fn run_commit_graph_write(
|
||||
split: bool,
|
||||
replace: bool,
|
||||
) -> Result<RepositoryMaintenanceResponse, tonic::Status> {
|
||||
tracing::info!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
split = split,
|
||||
replace = replace,
|
||||
"writing commit-graph"
|
||||
);
|
||||
let mut args: Vec<&str> = vec!["commit-graph", "write"];
|
||||
if split {
|
||||
args.push("--split");
|
||||
|
||||
@@ -9,8 +9,12 @@ impl tag_service_server::TagService for GitksService {
|
||||
request: tonic::Request<ListTagsRequest>,
|
||||
) -> Result<tonic::Response<ListTagsResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tag.list_tags", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_tags(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, count = resp.tags.len(), "list_tags done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -19,6 +23,10 @@ impl tag_service_server::TagService for GitksService {
|
||||
request: tonic::Request<GetTagRequest>,
|
||||
) -> Result<tonic::Response<Tag>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.get_tag", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_tag(inner).map_err(into_status)?;
|
||||
Ok(tonic::Response::new(resp))
|
||||
@@ -29,8 +37,13 @@ impl tag_service_server::TagService for GitksService {
|
||||
request: tonic::Request<CreateTagRequest>,
|
||||
) -> Result<tonic::Response<Tag>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.create_tag", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.create_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "tag created");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -39,8 +52,13 @@ impl tag_service_server::TagService for GitksService {
|
||||
request: tonic::Request<DeleteTagRequest>,
|
||||
) -> Result<tonic::Response<()>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.delete_tag", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
gb.delete_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, "tag deleted");
|
||||
Ok(tonic::Response::new(()))
|
||||
}
|
||||
|
||||
@@ -49,8 +67,13 @@ impl tag_service_server::TagService for GitksService {
|
||||
request: tonic::Request<VerifyTagRequest>,
|
||||
) -> Result<tonic::Response<VerifiedSignature>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let name = inner.name.clone();
|
||||
let span = tracing::info_span!("tag.verify_tag", %repo, %name);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.verify_tag(inner).map_err(into_status)?;
|
||||
tracing::info!(%repo, %name, verified = resp.verified, "tag verified");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
+68
-7
@@ -1,6 +1,6 @@
|
||||
use crate::pb::*;
|
||||
|
||||
use super::{GitksService, into_status, into_stream};
|
||||
use super::{GitksService, cache, into_status, into_stream};
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl tree_service_server::TreeService for GitksService {
|
||||
@@ -12,8 +12,18 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<ListTreeRequest>,
|
||||
) -> Result<tonic::Response<ListTreeResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.list_tree", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.list_tree(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.list_tree", &inner, || {
|
||||
gb.list_tree(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.list_tree(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.entries.len(), "list_tree done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -22,8 +32,17 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<GetTreeRequest>,
|
||||
) -> Result<tonic::Response<Tree>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_tree", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_tree(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_tree", &inner, || {
|
||||
gb.get_tree(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_tree(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -32,8 +51,18 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<GetBlobRequest>,
|
||||
) -> Result<tonic::Response<Blob>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let path = inner.path.clone();
|
||||
let span = tracing::info_span!("tree.get_blob", %repo, %path);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_blob(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_blob", &inner, || {
|
||||
gb.get_blob(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_blob(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -42,8 +71,21 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<GetRawBlobRequest>,
|
||||
) -> Result<tonic::Response<Self::GetRawBlobStream>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_raw_blob", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let items = gb.get_raw_blob(inner).map_err(into_status)?;
|
||||
let items = if inner.oid.is_some() {
|
||||
cache::cached_vec_response("tree.get_raw_blob", &inner, || {
|
||||
gb.get_raw_blob(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_vec_response("tree.get_raw_blob", &inner, || {
|
||||
gb.get_raw_blob(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_raw_blob(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(into_stream(items)))
|
||||
}
|
||||
|
||||
@@ -52,8 +94,17 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<GetFileMetadataRequest>,
|
||||
) -> Result<tonic::Response<FileMetadata>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.get_file_metadata", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.get_file_metadata(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.get_file_metadata", &inner, || {
|
||||
gb.get_file_metadata(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.get_file_metadata(inner).map_err(into_status)?
|
||||
};
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
|
||||
@@ -62,8 +113,18 @@ impl tree_service_server::TreeService for GitksService {
|
||||
request: tonic::Request<FindFilesRequest>,
|
||||
) -> Result<tonic::Response<FindFilesResponse>, tonic::Status> {
|
||||
let inner = request.into_inner();
|
||||
let repo = self.repo_label(inner.repository.as_ref());
|
||||
let span = tracing::info_span!("tree.find_files", %repo);
|
||||
let _enter = span.enter();
|
||||
let gb = self.resolve(inner.repository.as_ref())?;
|
||||
let resp = gb.find_files(inner).map_err(into_status)?;
|
||||
let resp = if cache::selector_is_oid(&inner.revision) {
|
||||
cache::cached_response("tree.find_files", &inner, || {
|
||||
gb.find_files(inner.clone()).map_err(into_status)
|
||||
})?
|
||||
} else {
|
||||
gb.find_files(inner).map_err(into_status)?
|
||||
};
|
||||
tracing::info!(%repo, count = resp.files.len(), "find_files done");
|
||||
Ok(tonic::Response::new(resp))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user