Skip to content
Clone

refactor(git): refresh crate across all submodules (impls, types, usecase)

zhenyi committed
parent 27a8619 commit 92c3b35 Browse files

66 files changed

+104 −578

File tree 66
Modified lib/git/analysis/impls.rs View lib/git/analysis/impls.rs
@@ -1,19 +1,12 @@
1//! analysis Operation trait for the analysis module (language/contribution stats on bare repositories based on gix).
2
31use crate::analysis::types::{CodeActivityStats, ContributorStat, LanguageStats, RefreshResult};
42
5/// Operation trait for the analysis module.
63pub trait AnalysisOps {
7 /// Compute repository language percentages (default branch).
8 fn analysis_languages(&self) -> Result<LanguageStats, crate::Error>;
9 /// Count contributors.
10 fn analysis_contributors(&self) -> Result<Vec<ContributorStat>, crate::Error>;
11 /// Code activity statistics.
12 fn analysis_code_activity_stats(
4 fn analysis_languages(&self) -> Result<LanguageStats, crate::Error>;
5 fn analysis_contributors(&self) -> Result<Vec<ContributorStat>, crate::Error>;
6 fn analysis_code_activity_stats(
137 &self,
148 from: Option<i64>,
159 branch: Option<&str>,
1610 ) -> Result<CodeActivityStats, crate::Error>;
17 /// Refresh analysis (incremental).
18 fn analysis_refresh(&self, commit_id: Option<&str>) -> Result<RefreshResult, crate::Error>;
11 fn analysis_refresh(&self, commit_id: Option<&str>) -> Result<RefreshResult, crate::Error>;
1912}
Modified lib/git/analysis/types.rs View lib/git/analysis/types.rs
@@ -1,31 +1,20 @@
1//! analysis Field types returned by the analysis module (aligned with Gitea models/repo/language_stats.go).
2
31use serde::{Deserialize, Serialize};
42
5/// A language statistic entry.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct LanguageStat {
8 /// Language name (e.g. "Rust"、"Go")。
9 pub language: String,
10 /// Percentage (0.0-100.0, computed from proportions at read time).
11 pub percentage: f32,
12 /// Size in bytes.
13 pub size: i64,
14 /// Whether this is the repository's primary language.
15 pub is_primary: bool,
16 /// The commit id the statistics correspond to (used to detect staleness).
17 pub commit_id: Option<String>,
18 /// Language display color (filled at runtime from a color table).
19 pub color: Option<String>,
5 pub language: String,
6 pub percentage: f32,
7 pub size: i64,
8 pub is_primary: bool,
9 pub commit_id: Option<String>,
10 pub color: Option<String>,
2011}
2112
22/// Set of language statistics.
2313#[derive(Debug, Clone, Serialize, Deserialize)]
2414pub struct LanguageStats {
2515 pub languages: Vec<LanguageStat>,
2616}
2717
28/// A contributor statistic entry.
2918#[derive(Debug, Clone, Serialize, Deserialize)]
3019pub struct ContributorStat {
3120 pub name: String,
@@ -35,7 +24,6 @@
3524 pub deletions: i64,
3625}
3726
38/// Repository code activity statistics (aligned with Gitea CodeActivityStats).
3927#[derive(Debug, Clone, Serialize, Deserialize)]
4028pub struct CodeActivityStats {
4129 pub authors: Vec<CodeActivityAuthor>,
@@ -44,7 +32,6 @@
4432 pub total_deletions: i64,
4533}
4634
47/// Per-author activity statistics.
4835#[derive(Debug, Clone, Serialize, Deserialize)]
4936pub struct CodeActivityAuthor {
5037 pub name: String,
@@ -54,7 +41,6 @@
5441 pub deletions: i64,
5542}
5643
57/// Repository analysis refresh result.
5844#[derive(Debug, Clone, Serialize, Deserialize)]
5945pub struct RefreshResult {
6046 pub refreshed: bool,
Modified lib/git/analysis/usecase.rs View lib/git/analysis/usecase.rs
@@ -1,10 +1,3 @@
1//! analysis usecase: implements AnalysisOps for GitRepository.
2//!
3//! Provides language stats, contributor stats, and code activity computed
4//! from a bare git repository using gix. Language detection here uses a
5//! simple extension-based heuristic; production deployments should plug
6//! a real language detector (e.g. enry) via a feature flag.
7
81use std::collections::HashMap;
92
103use crate::GitRepository;
@@ -14,8 +7,6 @@
147 RefreshResult,
158};
169
17/// Map a file extension to a language name. Extracted as a function so tests
18/// can override it.
1910fn ext_to_language(name: &str) -> Option<&'static str> {
2011 let ext = name.rsplit('.').next()?.to_ascii_lowercase();
2112 match ext.as_str() {
Modified lib/git/archive/impls.rs View lib/git/archive/impls.rs
@@ -1,11 +1,7 @@
1//! archive Operation method trait for the module.
2
31use crate::archive::types::{ArchiveOptions, ArchiveResult};
42
5/// Operation trait for the archive module.
63pub trait ArchiveOps {
7 /// Generate a repository archive (streaming).
8 fn archive_archive(
4 fn archive_archive(
95 &self,
106 repo: &str,
117 rev: &str,
Modified lib/git/archive/types.rs View lib/git/archive/types.rs
@@ -1,8 +1,5 @@
1//! archive Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Archive format.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74pub enum ArchiveFormat {
85 Tar,
@@ -11,18 +8,15 @@
118 TarZst,
129}
1310
14/// Archive request options.
1511#[derive(Debug, Clone, Serialize, Deserialize)]
1612pub struct ArchiveOptions {
1713 pub format: ArchiveFormat,
1814 pub path_filter: Option<String>,
1915}
2016
21/// Archive result (streaming).
2217#[derive(Debug, Clone, Serialize, Deserialize)]
2318pub struct ArchiveResult {
2419 pub format: ArchiveFormat,
2520 pub size: i64,
26 /// Cache key (used for StorageRepository caching).
27 pub cache_key: String,
21 pub cache_key: String,
2822}
Modified lib/git/archive/usecase.rs View lib/git/archive/usecase.rs
@@ -1,5 +1,3 @@
1//! archive usecase: generate a repository archive (streaming-stub).
2
31use bytes::Bytes;
42use storage::object_store::path::Path as StorePath;
53
@@ -7,7 +5,6 @@
75use crate::archive::impls::ArchiveOps;
86use crate::archive::types::{ArchiveFormat, ArchiveOptions, ArchiveResult};
97
10/// Map an archive format to its storage file extension.
118fn fmt_ext(format: ArchiveFormat) -> &'static str {
129 match format {
1310 ArchiveFormat::Tar => "tar",
@@ -17,7 +14,6 @@
1714 }
1815}
1916
20/// Map an archive format to the corresponding `git archive --format` value.
2117fn fmt_opt(format: ArchiveFormat) -> &'static str {
2218 match format {
2319 ArchiveFormat::Tar => "tar",
@@ -27,7 +23,6 @@
2723 }
2824}
2925
30/// Run `future` to completion on a dedicated runtime, returning its output.
3126fn block_on<F: std::future::Future>(fut: F) -> F::Output {
3227 tokio::runtime::Builder::new_current_thread()
3328 .enable_all()
@@ -52,13 +47,11 @@
5247 let storage = self.storage.clone();
5348 let cache_key = format!("{}:{}:{}", repo, rev, ext);
5449
55 // 1. Object-pool cache hit: fastest path, no storage roundtrip.
5650 if let Some(hit) = self.cache.object_pool_get_sync::<ArchiveResult>(&cache_key) {
5751 tracing::info!(rev, format = ?format, size = hit.size, "archive object-pool hit");
5852 return Ok(hit);
5953 }
6054
61 // 2. Storage hit: metadata present in object store.
6255 let exists = block_on(storage.exists(&storage_path))
6356 .map_err(|e| crate::Error::Other(format!("storage exists failed: {e}")))?;
6457 if exists {
@@ -77,7 +70,6 @@
7770 return Ok(v);
7871 }
7972
80 // 3. Cache miss: generate the archive and persist it to storage.
8173 let bytes =
8274 crate::gitcli::run_bytes(&path, &["archive", "--format", fmt_opt(format), rev])?;
8375 let size = bytes.len() as i64;
@@ -132,7 +124,6 @@
132124 let tmp = std::env::temp_dir().join(format!("git-archive-test-{}", std::process::id()));
133125 let _ = std::fs::remove_dir_all(&tmp);
134126 let repo = make_repo(&tmp, "main");
135 // Create a bare repo with a single commit via plumbing.
136127 crate::gitcli::run(&repo.path, &["init", "-q", "--bare", "-b", "main"]).unwrap();
137128 crate::gitcli::run(&repo.path, &["config", "user.email", "t@t"]).unwrap();
138129 crate::gitcli::run(&repo.path, &["config", "user.name", "t"]).unwrap();
Modified lib/git/blob/impls.rs View lib/git/blob/impls.rs
@@ -1,10 +1,7 @@
1//! blob Operation trait for the blob module (blob access on bare repositories based on gix).
2
31use gix::hash::ObjectId;
42
53use crate::blob::types::{BlobContent, BlobMeta, LfsPointer, RawDownload};
64
7/// blob Operation trait.
85pub trait BlobOps {
96 fn blob_content(&self, id: &ObjectId) -> Result<BlobContent, crate::Error>;
107 fn blob_metadata(&self, id: &ObjectId) -> Result<BlobMeta, crate::Error>;
Modified lib/git/blob/types.rs View lib/git/blob/types.rs
@@ -1,10 +1,7 @@
1//! blob Field types returned by the blob module (aligned with Gitea modules/git/blob.go).
2
31use serde::{Deserialize, Serialize};
42
53use crate::object::types::ObjectType;
64
7/// LFS LFS pointer (parsed from the git-lfs smudge format).
85#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct LfsPointer {
107 pub version: String,
@@ -12,7 +9,6 @@
129 pub size: i64,
1310}
1411
15/// blob Metadata.
1612#[derive(Debug, Clone, Serialize, Deserialize)]
1713pub struct BlobMeta {
1814 pub id: gix::hash::ObjectId,
@@ -23,7 +19,6 @@
2319 pub object_type: ObjectType,
2420}
2521
26/// Blob content result.
2722#[derive(Debug, Clone, Serialize, Deserialize)]
2823pub struct BlobContent {
2924 pub id: gix::hash::ObjectId,
@@ -32,7 +27,6 @@
3227 pub is_binary: bool,
3328}
3429
35/// Raw download result (streaming).
3630#[derive(Debug, Clone, Serialize, Deserialize)]
3731pub struct RawDownload {
3832 pub id: gix::hash::ObjectId,
Modified lib/git/blob/usecase.rs View lib/git/blob/usecase.rs
@@ -1,5 +1,3 @@
1//! blob Use case: implementation of `BlobOps` for `GitRepository`.
2
31use gix::hash::ObjectId;
42
53use crate::GitRepository;
Modified lib/git/commit/impls.rs View lib/git/commit/impls.rs
@@ -1,10 +1,7 @@
1//! commit Operation trait for the commit module (commit access on bare repositories based on gix).
2
31use gix::hash::ObjectId;
42
53use crate::commit::types::{Commit, CommitList, Signature};
64
7/// Operation trait for the commit module.
85pub trait CommitOps {
96 fn commit_commit(&self, rev: &str) -> Result<Commit, crate::Error>;
107 fn commit_by_id(&self, id: &ObjectId) -> Result<Commit, crate::Error>;
@@ -12,8 +9,7 @@
129 fn commit_tag_commit(&self, tag: &str) -> Result<Commit, crate::Error>;
1310 fn commit_list(&self, limit: u64, skip: u64) -> Result<CommitList, crate::Error>;
1411 fn commit_parent_count(&self, id: &ObjectId) -> Result<usize, crate::Error>;
15 /// Write a merge commit with the given parents and tree.
16 fn commit_write_merge(
12 fn commit_write_merge(
1713 &self,
1814 parents: Vec<ObjectId>,
1915 tree_id: ObjectId,
@@ -21,8 +17,7 @@
2117 author: &Signature,
2218 committer: &Signature,
2319 ) -> Result<ObjectId, crate::Error>;
24 /// Write a squash commit with a single parent.
25 fn commit_write_squash(
20 fn commit_write_squash(
2621 &self,
2722 parent: ObjectId,
2823 tree_id: ObjectId,
Modified lib/git/commit/types.rs View lib/git/commit/types.rs
@@ -1,8 +1,5 @@
1//! commit Field types returned by the commit module (aligned with Gitea modules/git Commit).
2
31use serde::{Deserialize, Serialize};
42
5/// Commit identity role.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
85pub enum IdentityRole {
@@ -11,7 +8,6 @@
118 CoAuthor,
129}
1310
14/// Commit identity (name/email pair).
1511#[derive(Debug, Clone, Serialize, Deserialize)]
1612pub struct CommitIdentity {
1713 pub name: String,
@@ -19,7 +15,6 @@
1915 pub role: IdentityRole,
2016}
2117
22/// Commit signature info.
2318#[derive(Debug, Clone, Serialize, Deserialize)]
2419pub struct CommitSignature {
2520 pub signature: String,
@@ -28,7 +23,6 @@
2823 pub valid: Option<bool>,
2924}
3025
31/// Git Signature (author/committer/tagger).
3226#[derive(Debug, Clone, Serialize, Deserialize)]
3327pub struct Signature {
3428 pub name: String,
@@ -48,14 +42,12 @@
4842 }
4943}
5044
51/// Trailer (e.g. Co-authored-by).
5245#[derive(Debug, Clone, Serialize, Deserialize)]
5346pub struct Trailer {
5447 pub key: String,
5548 pub value: String,
5649}
5750
58/// Commit message.
5951#[derive(Debug, Clone, Serialize, Deserialize)]
6052pub struct CommitMessage {
6153 pub raw: String,
@@ -110,7 +102,6 @@
110102 }
111103}
112104
113/// A commit object.
114105#[derive(Debug, Clone, Serialize, Deserialize)]
115106pub struct Commit {
116107 pub id: gix::hash::ObjectId,
@@ -144,7 +135,6 @@
144135 }
145136}
146137
147/// List of commits (paginated).
148138#[derive(Debug, Clone, Serialize, Deserialize)]
149139pub struct CommitList {
150140 pub commits: Vec<Commit>,
Modified lib/git/commit/usecase.rs View lib/git/commit/usecase.rs
@@ -1,5 +1,3 @@
1//! Commit module usecase: implements CommitOps for GitRepository on top of gix.
2
31use std::time::UNIX_EPOCH;
42
53use gix::hash::ObjectId;
Modified lib/git/conflict/impls.rs View lib/git/conflict/impls.rs
@@ -1,26 +1,19 @@
1//! conflict Operation trait for the conflict module (three-way merge checks based on gix).
2
31use crate::conflict::types::{ConflictDetail, MergeStatus};
42
5/// Operation trait for the conflict module.
63pub trait ConflictOps {
7 /// Check whether a merge is feasible.
8 fn conflict_check(&self, base: &str, head: &str) -> Result<MergeStatus, crate::Error>;
9 /// List of conflicted files.
10 fn conflict_conflicted_files(
4 fn conflict_check(&self, base: &str, head: &str) -> Result<MergeStatus, crate::Error>;
5 fn conflict_conflicted_files(
116 &self,
127 base: &str,
138 head: &str,
149 ) -> Result<Vec<String>, crate::Error>;
15 /// Single-file conflict details.
16 fn conflict_detail(
10 fn conflict_detail(
1711 &self,
1812 base: &str,
1913 head: &str,
2014 path: &str,
2115 ) -> Result<ConflictDetail, crate::Error>;
22 /// Three-way conflict check using an explicit merge-base ancestor.
23 fn conflict_check_threeway(
16 fn conflict_check_threeway(
2417 &self,
2518 ours: &str,
2619 theirs: &str,
Modified lib/git/conflict/types.rs View lib/git/conflict/types.rs
@@ -1,10 +1,7 @@
1//! conflict Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
53use crate::diff::types::DiffFile;
64
7/// A single conflicted file within a diff.
85#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ConflictFile {
107 pub path: String,
@@ -13,7 +10,6 @@
1310 pub base_oid: Option<gix::hash::ObjectId>,
1411}
1512
16/// Merge status result.
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1814pub struct MergeStatus {
1915 pub mergeable: bool,
@@ -21,7 +17,6 @@
2117 pub fast_forwardable: bool,
2218}
2319
24/// Conflict details.
2520#[derive(Debug, Clone, Serialize, Deserialize)]
2621pub struct ConflictDetail {
2722 pub path: String,
Modified lib/git/conflict/usecase.rs View lib/git/conflict/usecase.rs
@@ -1,5 +1,3 @@
1//! conflict Operation trait implementation for GitRepository.
2
31use std::collections::HashMap;
42
53use gix::object::tree::diff::ChangeDetached;
Modified lib/git/diff/impls.rs View lib/git/diff/impls.rs
@@ -1,8 +1,5 @@
1//! diff Operation trait for the diff module (diff access on bare repositories based on gix).
2
31use crate::diff::types::{Diff, DiffShortStat, DiffTreeRecord};
42
5/// diff Operation trait.
63pub trait DiffOps {
74 fn diff_diff(&self, base: &str, head: &str) -> Result<Diff, crate::Error>;
85 fn diff_short_stat(&self, base: &str, head: &str) -> Result<DiffShortStat, crate::Error>;
Modified lib/git/diff/types.rs View lib/git/diff/types.rs
@@ -1,8 +1,5 @@
1//! diff Field types returned by the diff module (aligned with Gitea modules/git Diff).
2
31use serde::{Deserialize, Serialize};
42
5/// diff Diff line type.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
85pub enum DiffLineType {
@@ -12,7 +9,6 @@
129 Section = 4,
1310}
1411
15/// diff A diff line.
1612#[derive(Debug, Clone, Serialize, Deserialize)]
1713pub struct DiffLine {
1814 pub left_idx: i32,
@@ -21,13 +17,11 @@
2117 pub content: String,
2218}
2319
24/// diff A diff section (hunk).
2520#[derive(Debug, Clone, Serialize, Deserialize)]
2621pub struct DiffSection {
2722 pub lines: Vec<DiffLine>,
2823}
2924
30/// diff Diff file type.
3125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3226#[serde(rename_all = "lowercase")]
3327pub enum DiffFileType {
@@ -38,7 +32,6 @@
3832 Copy = 5,
3933}
4034
41/// diff A file within a diff.
4235#[derive(Debug, Clone, Serialize, Deserialize)]
4336pub struct DiffFile {
4437 pub name: String,
@@ -67,7 +60,6 @@
6760 pub is_blob_type_csv: bool,
6861}
6962
70/// diff Result.
7163#[derive(Debug, Clone, Serialize, Deserialize)]
7264pub struct Diff {
7365 pub start: String,
@@ -76,7 +68,6 @@
7668 pub is_incomplete: bool,
7769}
7870
79/// Diff short stat.
8071#[derive(Debug, Clone, Serialize, Deserialize)]
8172pub struct DiffShortStat {
8273 pub num_files: i32,
@@ -84,7 +75,6 @@
8475 pub total_deletion: i32,
8576}
8677
87/// diff tree Record.
8878#[derive(Debug, Clone, Serialize, Deserialize)]
8979pub struct DiffTreeRecord {
9080 pub status: String,
Modified lib/git/diff/usecase.rs View lib/git/diff/usecase.rs
@@ -1,5 +1,3 @@
1//! diff Operation trait implementation for GitRepository.
2
31use std::convert::Infallible;
42use std::ops::ControlFlow;
53
@@ -7,7 +5,6 @@
75use crate::diff::impls::DiffOps;
86use crate::diff::types::{Diff, DiffFile, DiffFileType, DiffShortStat, DiffTreeRecord};
97
10/// Internal record for changes collected from the tree delta.
118#[derive(Debug, Clone)]
129struct ChangeRecord {
1310 status: String,
Modified lib/git/gc/impls.rs View lib/git/gc/impls.rs
@@ -1,17 +1,9 @@
1//! gc Operation method trait for the module.
2
31use crate::gc::types::{FsckResult, GcResult, PackStats, RepoStats};
42
5/// Operation trait for repository maintenance.
63pub trait GcOps {
7 /// Collect repository statistics.
8 fn gc_stats(&self) -> Result<RepoStats, crate::Error>;
9 /// Run garbage collection.
10 fn gc_gc(&self) -> Result<GcResult, crate::Error>;
11 /// Verify object integrity.
12 fn gc_fsck(&self) -> Result<FsckResult, crate::Error>;
13 /// Object count.
14 fn gc_count_objects(&self) -> Result<PackStats, crate::Error>;
15 /// Prune dangling objects.
16 fn gc_prune_orphans(&self) -> Result<GcResult, crate::Error>;
4 fn gc_stats(&self) -> Result<RepoStats, crate::Error>;
5 fn gc_gc(&self) -> Result<GcResult, crate::Error>;
6 fn gc_fsck(&self) -> Result<FsckResult, crate::Error>;
7 fn gc_count_objects(&self) -> Result<PackStats, crate::Error>;
8 fn gc_prune_orphans(&self) -> Result<GcResult, crate::Error>;
179}
Modified lib/git/gc/types.rs View lib/git/gc/types.rs
@@ -1,8 +1,5 @@
1//! gc Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Repository statistics (aligned with Gitea's separate Size/GitSize/LFSSize billing).
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct RepoStats {
85 pub size: i64,
@@ -13,14 +10,12 @@
1310 pub pack_count: u64,
1411}
1512
16/// fsck Result.
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1814pub struct FsckResult {
1915 pub ok: bool,
2016 pub errors: Vec<String>,
2117}
2218
23/// gc Result.
2419#[derive(Debug, Clone, Serialize, Deserialize)]
2520pub struct GcResult {
2621 pub pruned: u64,
@@ -28,7 +23,6 @@
2823 pub message: String,
2924}
3025
31/// pack Statistics.
3226#[derive(Debug, Clone, Serialize, Deserialize)]
3327pub struct PackStats {
3428 pub objects: u64,
Modified lib/git/gc/usecase.rs View lib/git/gc/usecase.rs
@@ -1,5 +1,3 @@
1//! gc usecase: repository maintenance.
2
31use crate::GitRepository;
42use crate::gc::impls::GcOps;
53use crate::gc::types::{FsckResult, GcResult, PackStats, RepoStats};
@@ -56,7 +54,6 @@
5654 };
5755 match repo.find_object(id) {
5856 Ok(obj) => {
59 // Verify the object's checksum matches its id.
6057 let kind = obj.kind;
6158 let data = obj.data.clone();
6259 let computed = gix::objs::Write::write_buf(&repo, kind, &data)
@@ -112,7 +109,6 @@
112109 }
113110}
114111
115/// Walk the loose-objects directory and count files.
116112fn walk_loose_objects(repo: &gix::Repository) -> u64 {
117113 let mut count = 0u64;
118114 let dir = repo.path().join("objects");
Modified lib/git/hook/impls.rs View lib/git/hook/impls.rs
@@ -1,21 +1,13 @@
1//! hook Operation method trait for the module.
2
31use crate::hook::types::{HookEvent, HookEventKind, HookResult, ScriptOutput};
42
5/// hook Operation trait.
63pub trait HookOps {
7 /// Run pre-receive validation (rejects on error).
8 fn hook_pre_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error>;
9 /// Run the post-receive event.
10 fn hook_post_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error>;
11 /// Emit an event (e.g. webhook delivery).
12 fn hook_emit(&self, event: &HookEvent) -> Result<(), crate::Error>;
13 /// Execute an external script.
14 fn hook_execute_script(
4 fn hook_pre_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error>;
5 fn hook_post_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error>;
6 fn hook_emit(&self, event: &HookEvent) -> Result<(), crate::Error>;
7 fn hook_execute_script(
158 &self,
169 script: &str,
1710 args: &[&str],
1811 ) -> Result<ScriptOutput, crate::Error>;
19 /// Check whether an event type is supported.
20 fn hook_supported(&self, kind: HookEventKind) -> bool;
12 fn hook_supported(&self, kind: HookEventKind) -> bool;
2113}
Modified lib/git/hook/types.rs View lib/git/hook/types.rs
@@ -1,8 +1,5 @@
1//! hook Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Hook Event type.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74pub enum HookEventKind {
85 PreReceive,
@@ -13,7 +10,6 @@
1310 MergeRequest,
1411}
1512
16/// Hook Event.
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1814pub struct HookEvent {
1915 pub kind: HookEventKind,
@@ -23,7 +19,6 @@
2319 pub timestamp: i64,
2420}
2521
26/// Hook Reference change.
2722#[derive(Debug, Clone, Serialize, Deserialize)]
2823pub struct HookRef {
2924 pub name: String,
@@ -31,14 +26,12 @@
3126 pub new_oid: Option<gix::hash::ObjectId>,
3227}
3328
34/// Hook execution result.
3529#[derive(Debug, Clone, Serialize, Deserialize)]
3630pub struct HookResult {
3731 pub accepted: bool,
3832 pub message: Option<String>,
3933}
4034
41/// Script execution output.
4235#[derive(Debug, Clone, Serialize, Deserialize)]
4336pub struct ScriptOutput {
4437 pub exit_code: i32,
Modified lib/git/hook/usecase.rs View lib/git/hook/usecase.rs
@@ -1,17 +1,9 @@
1//! hook usecase: implements HookOps for GitRepository.
2//!
3//! Hooks are external to bare git operations; here we offer a default
4//! implementation that always accepts (no-op) and a helper to run
5//! arbitrary scripts so callers can plug in their policy engine.
6
71use crate::GitRepository;
82use crate::hook::impls::HookOps;
93use crate::hook::types::{HookEvent, HookEventKind, HookRef, HookResult, ScriptOutput};
104
115impl HookOps for GitRepository {
126 fn hook_pre_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error> {
13 // Fail-closed: without a configured policy engine, reject the push
14 // rather than silently accepting every ref update.
157 tracing::warn!(repo_id = event.repo_id, actor = ?event.actor, "pre-receive rejected: no hook policy configured");
168 Ok(HookResult {
179 accepted: false,
@@ -20,8 +12,6 @@
2012 }
2113
2214 fn hook_post_receive(&self, event: &HookEvent) -> Result<HookResult, crate::Error> {
23 // Post-receive is not an authorization gate; report it but do not
24 // fabricate acceptance.
2515 tracing::info!(repo_id = event.repo_id, actor = ?event.actor, "post-receive event recorded");
2616 Ok(HookResult {
2717 accepted: true,
@@ -40,7 +30,6 @@
4030 args: &[&str],
4131 ) -> Result<ScriptOutput, crate::Error> {
4232 // Only allow scripts from a trusted, fixed directory to prevent
43 // arbitrary path execution from untrusted input.
4433 let name = std::path::Path::new(script);
4534 if name.is_absolute()
4635 || name.components().any(|c| {
@@ -84,7 +73,6 @@
8473 }
8574}
8675
87/// Helper to build a `HookRef` change record.
8876#[allow(dead_code)]
8977pub fn make_hook_ref(name: &str, old_oid: Option<&str>, new_oid: Option<&str>) -> HookRef {
9078 let old_oid = old_oid.and_then(|s| gix::hash::ObjectId::from_hex(s.as_bytes()).ok());
Modified lib/git/lfs/impls.rs View lib/git/lfs/impls.rs
@@ -1,26 +1,15 @@
1//! lfs Operation method trait for the module.
2
31use crate::lfs::types::{
42 LfsBatchRequest, LfsBatchResponse, LfsLock, LfsLockList, LfsObject, LfsUploadResult,
53};
64
7/// LFS Operation trait.
85pub trait LfsOps {
9 /// Handle an LFS batch request.
10 fn lfs_handle_batch(&self, req: &LfsBatchRequest) -> Result<LfsBatchResponse, crate::Error>;
11 /// Upload an LFS object.
12 fn lfs_upload(&self, obj: &LfsObject) -> Result<LfsUploadResult, crate::Error>;
13 /// Download an LFS object (returns an href).
14 fn lfs_download(&self, obj: &LfsObject) -> Result<String, crate::Error>;
15 /// Verify an LFS object.
16 fn lfs_verify(&self, obj: &LfsObject) -> Result<(), crate::Error>;
17 /// Acquire a lock.
18 fn lfs_lock(&self, path: &str, owner: &str) -> Result<LfsLock, crate::Error>;
19 /// Release a lock.
20 fn lfs_unlock(&self, path: &str, owner: &str) -> Result<LfsLock, crate::Error>;
21 /// List locks.
22 fn lfs_list_locks(&self, cursor: Option<&str>, limit: u64)
6 fn lfs_handle_batch(&self, req: &LfsBatchRequest) -> Result<LfsBatchResponse, crate::Error>;
7 fn lfs_upload(&self, obj: &LfsObject) -> Result<LfsUploadResult, crate::Error>;
8 fn lfs_download(&self, obj: &LfsObject) -> Result<String, crate::Error>;
9 fn lfs_verify(&self, obj: &LfsObject) -> Result<(), crate::Error>;
10 fn lfs_lock(&self, path: &str, owner: &str) -> Result<LfsLock, crate::Error>;
11 fn lfs_unlock(&self, path: &str, owner: &str) -> Result<LfsLock, crate::Error>;
12 fn lfs_list_locks(&self, cursor: Option<&str>, limit: u64)
2313 -> Result<LfsLockList, crate::Error>;
24 /// Clean up unreferenced objects.
25 fn lfs_cleanup_unreferenced(&self) -> Result<u64, crate::Error>;
14 fn lfs_cleanup_unreferenced(&self) -> Result<u64, crate::Error>;
2615}
Modified lib/git/lfs/types.rs View lib/git/lfs/types.rs
@@ -1,8 +1,5 @@
1//! lfs Field types returned by the lfs module (aligned with the batch payload in modules/lfs/shared.go).
2
31use serde::{Deserialize, Serialize};
42
5/// LFS An object.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct LfsObject {
85 pub oid: String,
@@ -10,7 +7,6 @@
107 pub content_type: Option<String>,
118}
129
13/// LFS Operation type.
1410#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1511#[serde(rename_all = "lowercase")]
1612pub enum LfsOperation {
@@ -19,7 +15,6 @@
1915 Verify,
2016}
2117
22/// batch Request.
2318#[derive(Debug, Clone, Serialize, Deserialize)]
2419pub struct LfsBatchRequest {
2520 pub operation: LfsOperation,
@@ -27,7 +22,6 @@
2722 pub objects: Vec<LfsObject>,
2823}
2924
30/// A single object action within an LFS batch.
3125#[derive(Debug, Clone, Serialize, Deserialize)]
3226pub struct LfsAction {
3327 pub href: String,
@@ -35,7 +29,6 @@
3529 pub expires_at: Option<String>,
3630}
3731
38/// A single-object batch result.
3932#[derive(Debug, Clone, Serialize, Deserialize)]
4033pub struct LfsBatchObject {
4134 pub oid: String,
@@ -44,21 +37,18 @@
4437 pub error: Option<LfsError>,
4538}
4639
47/// LFS Error.
4840#[derive(Debug, Clone, Serialize, Deserialize)]
4941pub struct LfsError {
5042 pub code: i32,
5143 pub message: String,
5244}
5345
54/// batch Response.
5546#[derive(Debug, Clone, Serialize, Deserialize)]
5647pub struct LfsBatchResponse {
5748 pub transfer: String,
5849 pub objects: Vec<LfsBatchObject>,
5950}
6051
61/// LFS A lock.
6252#[derive(Debug, Clone, Serialize, Deserialize)]
6353pub struct LfsLock {
6454 pub path: String,
@@ -66,14 +56,12 @@
6656 pub oid: Option<String>,
6757}
6858
69/// List of locks.
7059#[derive(Debug, Clone, Serialize, Deserialize)]
7160pub struct LfsLockList {
7261 pub locks: Vec<LfsLock>,
7362 pub next_cursor: Option<String>,
7463}
7564
76/// Upload result.
7765#[derive(Debug, Clone, Serialize, Deserialize)]
7866pub struct LfsUploadResult {
7967 pub present: bool,
Modified lib/git/lfs/usecase.rs View lib/git/lfs/usecase.rs
@@ -1,6 +1,3 @@
1//! lfs usecase: LFS batch handling. Physical storage is fully delegated to
2//! `lib/gitlfs` via `GitRepository::lfs`, decoupled from the Git object store.
3
41use std::collections::HashMap;
52
63use gitlfs::LfsOid;
@@ -143,7 +140,6 @@
143140 actions
144141}
145142
146/// Bridge the sync `LfsOps` trait to the async `LfsObjectStore`.
147143fn block_on_safe<F, T>(f: impl FnOnce() -> F) -> Result<T, crate::Error>
148144where
149145 F: std::future::Future<Output = Result<T, gitlfs::LfsError>>,
Modified lib/git/log/impls.rs View lib/git/log/impls.rs
@@ -1,21 +1,14 @@
1//! log Operation method trait for the module.
2
31use crate::commit::types::Commit;
42use crate::log::types::{CommitGraph, LogList, LogOptions};
53
6/// Operation trait for the commit log module.
74pub trait LogOps {
8 /// List commits (cursor pagination).
9 fn log_list(&self, opts: &LogOptions) -> Result<LogList, crate::Error>;
10 /// Commits between two commits.
11 fn log_commits_between(
5 fn log_list(&self, opts: &LogOptions) -> Result<LogList, crate::Error>;
6 fn log_commits_between(
127 &self,
138 after: &str,
149 before: &str,
1510 limit: u64,
1611 ) -> Result<LogList, crate::Error>;
17 /// Commit graph.
18 fn log_graph(&self, opts: &LogOptions) -> Result<CommitGraph, crate::Error>;
19 /// Last commit on a branch.
20 fn log_last_commit(&self, branch_name: String) -> Result<Commit, crate::Error>;
12 fn log_graph(&self, opts: &LogOptions) -> Result<CommitGraph, crate::Error>;
13 fn log_last_commit(&self, branch_name: String) -> Result<Commit, crate::Error>;
2114}
Modified lib/git/log/types.rs View lib/git/log/types.rs
@@ -1,32 +1,25 @@
1//! log Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
53use crate::commit::types::Commit;
64
7/// Commit log list.
85#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct LogList {
107 pub commits: Vec<Commit>,
11 /// Cursor (id of the last commit) for pagination.
12 pub cursor: Option<String>,
8 pub cursor: Option<String>,
139 pub has_more: bool,
1410}
1511
16/// Commit graph node.
1712#[derive(Debug, Clone, Serialize, Deserialize)]
1813pub struct GraphNode {
1914 pub commit: Commit,
2015 pub lanes: Vec<usize>,
2116}
2217
23/// Commit graph.
2418#[derive(Debug, Clone, Serialize, Deserialize)]
2519pub struct CommitGraph {
2620 pub nodes: Vec<GraphNode>,
2721}
2822
29/// Log query options.
3023#[derive(Debug, Clone, Serialize, Deserialize)]
3124pub struct LogOptions {
3225 pub limit: Option<u64>,
Modified lib/git/log/usecase.rs View lib/git/log/usecase.rs
@@ -1,5 +1,3 @@
1//! log usecase: implements LogOps for GitRepository.
2
31use crate::GitRepository;
42use crate::commit::types::Commit;
53use crate::log::impls::LogOps;
Modified lib/git/object/cache.rs View lib/git/object/cache.rs
@@ -10,8 +10,6 @@
1010 pub expire_ttl: u64,
1111}
1212
13/// Sanitize a cache key so it cannot collide across namespaces or inject
14/// separator characters into the underlying cache storage.
1513pub(crate) fn sanitize_key(k: &str) -> String {
1614 let mut out = String::with_capacity(k.len());
1715 for b in k.bytes() {
@@ -56,13 +54,6 @@
5654 Some(())
5755 }
5856
59 // ---- Sync bridge -----------------------------------------------------
60 //
61 // The trait methods in `lib/git/*/usecase.rs` are sync (`fn`), but the
62 // cache backend is async. We bridge by blocking on the future. If a tokio
63 // runtime is already running on this thread we reuse its handle; otherwise
64 // we build a fresh single-thread runtime. This avoids panics when callers
65 // invoke these trait methods from inside an async context.
6657
6758 pub fn get_sync<T: DeserializeOwned + Send + Sync>(&self, k: &str) -> Option<T> {
6859 let cache = self.cache.clone();
@@ -135,9 +126,7 @@
135126 });
136127 }
137128
138 /// Sync variant of `set` with explicit TTL. Use this for content-addressed
139 /// entries (TTL=0 = never expires).
140 pub fn set_sync_with_ttl<V: Serialize>(&self, k: &str, v: V, ttl: u64) {
129 pub fn set_sync_with_ttl<V: Serialize>(&self, k: &str, v: V, ttl: u64) {
141130 let cache = self.cache.clone();
142131 let prefix = self.prefix.clone();
143132 let key = format!("{}.{}", prefix, sanitize_key(k));
Modified lib/git/object/impls.rs View lib/git/object/impls.rs
@@ -1,10 +1,7 @@
1//! object Operation trait for the object module (object access on bare repositories based on gix).
2
31use gix::hash::ObjectId;
42
53use crate::object::types::{ObjectFormat, ObjectInfo, ObjectType, WriteObjectResult};
64
7/// Operation trait for the object module.
85pub trait ObjectOps {
96 fn object_format(&self) -> Result<ObjectFormat, crate::Error>;
107 fn object_exists(&self, id: &ObjectId) -> Result<bool, crate::Error>;
Modified lib/git/object/types.rs View lib/git/object/types.rs
@@ -1,8 +1,5 @@
1//! object Field types returned by the object module (aligned with Gitea modules/git object capabilities).
2
31use serde::{Deserialize, Serialize};
42
5/// Object format (SHA-1 / SHA-256).
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "lowercase")]
85pub enum ObjectFormat {
@@ -20,7 +17,6 @@
2017 }
2118}
2219
23/// Object type.
2420#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2521#[serde(rename_all = "lowercase")]
2622pub enum ObjectType {
@@ -41,7 +37,6 @@
4137 }
4238}
4339
44/// Object query result.
4540#[derive(Debug, Clone, Serialize, Deserialize)]
4641pub struct ObjectInfo {
4742 pub id: gix::hash::ObjectId,
@@ -49,7 +44,6 @@
4944 pub size: u64,
5045}
5146
52/// Result of writing an object.
5347#[derive(Debug, Clone, Serialize, Deserialize)]
5448pub struct WriteObjectResult {
5549 pub id: gix::hash::ObjectId,
Modified lib/git/object/usecase.rs View lib/git/object/usecase.rs
@@ -1,5 +1,3 @@
1//! object Use-case implementations for [`GitRepository`](crate::GitRepository).
2
31use gix::hash::ObjectId;
42
53use crate::GitRepository;
Modified lib/git/pack/impls.rs View lib/git/pack/impls.rs
@@ -1,17 +1,9 @@
1//! pack Operation method trait for the module.
2
31use crate::pack::types::{HotPackResult, IndexResult, IngestPackResult, PackStats, RepackResult};
42
5/// pack Operation trait.
63pub trait PackOps {
7 /// Receive and parse a pack stream.
8 fn pack_ingest_pack(&self, data: &[u8]) -> Result<IngestPackResult, crate::Error>;
9 /// Build a pack index.
10 fn pack_index(&self, pack_id: &str) -> Result<IndexResult, crate::Error>;
11 /// Repack the repository.
12 fn pack_repack(&self) -> Result<RepackResult, crate::Error>;
13 /// pack Statistics.
14 fn pack_stats(&self) -> Result<PackStats, crate::Error>;
15 /// Maintain a hot pack cache.
16 fn pack_cache_hot_pack(&self, pack_id: &str) -> Result<HotPackResult, crate::Error>;
4 fn pack_ingest_pack(&self, data: &[u8]) -> Result<IngestPackResult, crate::Error>;
5 fn pack_index(&self, pack_id: &str) -> Result<IndexResult, crate::Error>;
6 fn pack_repack(&self) -> Result<RepackResult, crate::Error>;
7 fn pack_stats(&self) -> Result<PackStats, crate::Error>;
8 fn pack_cache_hot_pack(&self, pack_id: &str) -> Result<HotPackResult, crate::Error>;
179}
Modified lib/git/pack/types.rs View lib/git/pack/types.rs
@@ -1,8 +1,5 @@
1//! pack Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Result of ingesting a pack.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct IngestPackResult {
85 pub object_count: u64,
@@ -10,21 +7,18 @@
107 pub delta_count: u64,
118}
129
13/// Index result.
1410#[derive(Debug, Clone, Serialize, Deserialize)]
1511pub struct IndexResult {
1612 pub pack_id: String,
1713 pub object_count: u64,
1814}
1915
20/// Repack result.
2116#[derive(Debug, Clone, Serialize, Deserialize)]
2217pub struct RepackResult {
2318 pub packs: Vec<String>,
2419 pub pruned: u64,
2520}
2621
27/// pack Statistics.
2822#[derive(Debug, Clone, Serialize, Deserialize)]
2923pub struct PackStats {
3024 pub objects: u64,
@@ -32,7 +26,6 @@
3226 pub delta_count: u64,
3327}
3428
35/// Hot pack cache result.
3629#[derive(Debug, Clone, Serialize, Deserialize)]
3730pub struct HotPackResult {
3831 pub cached: bool,
Modified lib/git/pack/usecase.rs View lib/git/pack/usecase.rs
@@ -1,5 +1,3 @@
1//! pack usecase: pack ingestion and indexing.
2
31use crate::GitRepository;
42use crate::pack::impls::PackOps;
53use crate::pack::types::{HotPackResult, IndexResult, IngestPackResult, PackStats, RepackResult};
@@ -27,7 +25,6 @@
2725 fn pack_ingest_pack(&self, data: &[u8]) -> Result<IngestPackResult, crate::Error> {
2826 let repo = self.repo()?;
2927 let path = repo.path().to_path_buf();
30 // Index and install the pack into the object database via `git index-pack`.
3128 let out = duct::cmd("git", &["index-pack", "--stdin"])
3229 .env("GIT_DIR", &path)
3330 .env("GIT_TERMINAL_PROMPT", "0")
@@ -93,8 +90,6 @@
9390 }
9491
9592 fn pack_cache_hot_pack(&self, pack_id: &str) -> Result<HotPackResult, crate::Error> {
96 // Hot-pack caching is a bespoke feature; fall back to a repack so the
97 // object data is consolidated, then report the pack id.
9893 let repo = self.repo()?;
9994 let path = repo.path().to_path_buf();
10095 crate::gitcli::run(&path, &["repack", "-a", "-d", "-q"])?;
Modified lib/git/patch/impls.rs View lib/git/patch/impls.rs
@@ -1,13 +1,7 @@
1//! patch Operation method trait for the module.
2
31use crate::patch::types::{ApplyResult, ParsedPatch, Patch};
42
5/// Operation trait for the patch module.
63pub trait PatchOps {
7 /// Generate a unified diff patch.
8 fn patch_generate(&self, base: &str, head: &str) -> Result<Patch, crate::Error>;
9 /// Parse a patch.
10 fn patch_parse(&self, content: &str) -> Result<ParsedPatch, crate::Error>;
11 /// Apply a patch.
12 fn patch_apply(&self, patch: &str, base: &str) -> Result<ApplyResult, crate::Error>;
4 fn patch_generate(&self, base: &str, head: &str) -> Result<Patch, crate::Error>;
5 fn patch_parse(&self, content: &str) -> Result<ParsedPatch, crate::Error>;
6 fn patch_apply(&self, patch: &str, base: &str) -> Result<ApplyResult, crate::Error>;
137}
Modified lib/git/patch/types.rs View lib/git/patch/types.rs
@@ -1,22 +1,17 @@
1//! patch Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Patch generation result.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct Patch {
85 pub content: String,
96 pub size: i64,
107}
118
12/// Patch apply result.
139#[derive(Debug, Clone, Serialize, Deserialize)]
1410pub struct ApplyResult {
1511 pub applied: bool,
1612 pub conflicts: Vec<String>,
1713}
1814
19/// Patch parse result.
2015#[derive(Debug, Clone, Serialize, Deserialize)]
2116pub struct ParsedPatch {
2217 pub files: Vec<String>,
Modified lib/git/patch/usecase.rs View lib/git/patch/usecase.rs
@@ -1,5 +1,3 @@
1//! patch usecase: unified diff generation.
2
31use crate::GitRepository;
42use crate::patch::impls::PatchOps;
53use crate::patch::types::{ApplyResult, ParsedPatch, Patch};
@@ -66,7 +64,6 @@
6664 files.push(path.trim_start_matches("b/").to_string());
6765 }
6866 } else if line.starts_with("+++") || line.starts_with("---") {
69 // skip
7067 } else if line.strip_prefix('+').is_some() {
7168 additions += 1;
7269 } else if line.strip_prefix('-').is_some() {
@@ -83,8 +80,6 @@
8380 fn patch_apply(&self, patch: &str, base: &str) -> Result<ApplyResult, crate::Error> {
8481 let repo = self.repo()?;
8582 let repo_path = repo.path().to_path_buf();
86 // Stage the patch in a temp worktree checked out at `base`, then apply
87 // it with `git apply`. This mirrors `git apply` semantics without a shell.
8883 let tmp = std::env::temp_dir().join(format!(
8984 "git-patch-{}-{}",
9085 std::process::id(),
@@ -97,7 +92,6 @@
9792 let cleanup = || {
9893 let _ = std::fs::remove_dir_all(&tmp);
9994 };
100 // Clone (no checkout) then checkout the base revision.
10195 let init = duct::cmd("git", &["init", "-q"])
10296 .dir(&tmp)
10397 .run()
Modified lib/git/pipeline/impls.rs View lib/git/pipeline/impls.rs
@@ -1,27 +1,19 @@
1//! pipeline Operation method trait for the module.
2
31use gix::hash::ObjectId;
42
53use crate::pipeline::types::{CommitStatus, Job, StatusList, TriggerResult};
64
7/// Operation trait for the pipeline module.
85pub trait PipelineOps {
9 /// Trigger a pipeline.
10 fn pipeline_trigger(
6 fn pipeline_trigger(
117 &self,
128 commit: &ObjectId,
139 branch: &str,
1410 ) -> Result<TriggerResult, crate::Error>;
15 /// Schedule a job.
16 fn pipeline_schedule(&self, commit: &ObjectId, job: &Job) -> Result<(), crate::Error>;
17 /// Set a commit status.
18 fn pipeline_set_commit_status(
11 fn pipeline_schedule(&self, commit: &ObjectId, job: &Job) -> Result<(), crate::Error>;
12 fn pipeline_set_commit_status(
1913 &self,
2014 commit: &ObjectId,
2115 status: &CommitStatus,
2216 ) -> Result<(), crate::Error>;
23 /// List commit statuses.
24 fn pipeline_list_statuses(&self, commit: &ObjectId) -> Result<StatusList, crate::Error>;
25 /// Cancel a job.
26 fn pipeline_cancel(&self, job_id: &str) -> Result<(), crate::Error>;
17 fn pipeline_list_statuses(&self, commit: &ObjectId) -> Result<StatusList, crate::Error>;
18 fn pipeline_cancel(&self, job_id: &str) -> Result<(), crate::Error>;
2719}
Modified lib/git/pipeline/types.rs View lib/git/pipeline/types.rs
@@ -1,8 +1,5 @@
1//! pipeline Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Commit status.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74pub enum CommitStatusState {
85 Pending,
@@ -13,7 +10,6 @@
1310 Skipped,
1411}
1512
16/// A commit status entry.
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1814pub struct CommitStatus {
1915 pub state: CommitStatusState,
@@ -23,7 +19,6 @@
2319 pub creator: Option<String>,
2420}
2521
26/// Job.
2722#[derive(Debug, Clone, Serialize, Deserialize)]
2823pub struct Job {
2924 pub id: String,
@@ -33,14 +28,12 @@
3328 pub finished_at: Option<i64>,
3429}
3530
36/// Trigger result.
3731#[derive(Debug, Clone, Serialize, Deserialize)]
3832pub struct TriggerResult {
3933 pub job_id: String,
4034 pub queued: bool,
4135}
4236
43/// Status list.
4437#[derive(Debug, Clone, Serialize, Deserialize)]
4538pub struct StatusList {
4639 pub statuses: Vec<CommitStatus>,
Modified lib/git/pipeline/usecase.rs View lib/git/pipeline/usecase.rs
@@ -1,5 +1,3 @@
1//! pipeline usecase: in-memory pipeline stub (status cache only).
2
31use gix::hash::ObjectId;
42
53use crate::GitRepository;
Modified lib/git/refs/impls.rs View lib/git/refs/impls.rs
@@ -1,38 +1,24 @@
1//! refs Operation trait for the refs module (reference access on bare repositories based on gix).
2
31use gix::hash::ObjectId;
42
53use crate::refs::types::{BranchInfo, HeadInfo, RefInfo, RefType, UpdateRefResult};
64
7/// Operation trait for the refs module.
85pub trait RefsOps {
96 fn refs_list(&self) -> Result<Vec<RefInfo>, crate::Error>;
10 /// List references by pattern.
11 fn refs_list_filtered(&self, pattern: &str) -> Result<Vec<RefInfo>, crate::Error>;
12 /// Get a single reference.
13 fn refs_get(&self, name: &str) -> Result<RefInfo, crate::Error>;
14 /// Resolve a reference to an object id.
15 fn refs_resolve(&self, name: &str) -> Result<ObjectId, crate::Error>;
16 /// Update a reference (CAS; returns Err on failure).
17 fn refs_update(
7 fn refs_list_filtered(&self, pattern: &str) -> Result<Vec<RefInfo>, crate::Error>;
8 fn refs_get(&self, name: &str) -> Result<RefInfo, crate::Error>;
9 fn refs_resolve(&self, name: &str) -> Result<ObjectId, crate::Error>;
10 fn refs_update(
1811 &self,
1912 name: &str,
2013 new: ObjectId,
2114 expected: Option<ObjectId>,
2215 ) -> Result<UpdateRefResult, crate::Error>;
23 /// Delete a reference.
24 fn refs_delete(&self, name: &str) -> Result<(), crate::Error>;
25 /// Validate a reference name.
26 fn refs_validate_name(&self, name: &str) -> Result<(), crate::Error>;
27 /// Whether a branch is protected.
28 fn refs_is_branch_protected(&self, branch: &str) -> Result<bool, crate::Error>;
29 /// HEAD A reference.
30 fn refs_head(&self) -> Result<HeadInfo, crate::Error>;
31 /// List of branches.
32 fn refs_branches(&self, skip: u64, limit: u64) -> Result<Vec<BranchInfo>, crate::Error>;
33 /// Whether an object exists in a reference.
34 fn refs_is_object_in_ref(&self, object: &ObjectId, refname: &str)
16 fn refs_delete(&self, name: &str) -> Result<(), crate::Error>;
17 fn refs_validate_name(&self, name: &str) -> Result<(), crate::Error>;
18 fn refs_is_branch_protected(&self, branch: &str) -> Result<bool, crate::Error>;
19 fn refs_head(&self) -> Result<HeadInfo, crate::Error>;
20 fn refs_branches(&self, skip: u64, limit: u64) -> Result<Vec<BranchInfo>, crate::Error>;
21 fn refs_is_object_in_ref(&self, object: &ObjectId, refname: &str)
3522 -> Result<bool, crate::Error>;
36 /// Classify a reference type.
37 fn refs_classify(&self, name: &str) -> RefType;
23 fn refs_classify(&self, name: &str) -> RefType;
3824}
Modified lib/git/refs/types.rs View lib/git/refs/types.rs
@@ -1,10 +1,7 @@
1//! refs Field types returned by the refs module (aligned with Gitea modules/git/ref.go and branch.go).
2
31use serde::{Deserialize, Serialize};
42
53use crate::object::types::ObjectType;
64
7/// Reference type.
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub enum RefType {
107 Branch,
@@ -13,7 +10,6 @@
1310 Other,
1411}
1512
16/// Reference info.
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1814pub struct RefInfo {
1915 pub name: String,
@@ -21,7 +17,6 @@
2117 pub r#type: RefType,
2218}
2319
24/// Reference update result.
2520#[derive(Debug, Clone, Serialize, Deserialize)]
2621pub struct UpdateRefResult {
2722 pub name: String,
@@ -29,7 +24,6 @@
2924 pub new_oid: gix::hash::ObjectId,
3025}
3126
32/// Branch info.
3327#[derive(Debug, Clone, Serialize, Deserialize)]
3428pub struct BranchInfo {
3529 pub name: String,
@@ -38,7 +32,6 @@
3832 pub is_protected: bool,
3933}
4034
41/// HEAD A reference.
4235#[derive(Debug, Clone, Serialize, Deserialize)]
4336pub struct HeadInfo {
4437 pub name: String,
Modified lib/git/refs/usecase.rs View lib/git/refs/usecase.rs
@@ -1,5 +1,3 @@
1//! refs usecase: implements RefsOps for GitRepository on top of gix.
2
31use gix::hash::ObjectId;
42use gix::refs::transaction::{Change, LogChange, PreviousValue, RefEdit, RefLog};
53use gix::refs::{Category, FullName, Target};
@@ -30,12 +28,10 @@
3028 }
3129}
3230
33/// Extract the short branch name from a `refs/heads/{short}` name, if any.
3431fn branch_short(name: &str) -> Option<&str> {
3532 name.strip_prefix("refs/heads/")
3633}
3734
38/// Return an error if the given ref is a protected branch.
3935fn guard_protected_ref(
4036 policy: &dyn crate::policy::PolicyProvider,
4137 name: &str,
Modified lib/git/remote/impls.rs View lib/git/remote/impls.rs
@@ -1,29 +1,21 @@
1//! remote Operation method trait for the module.
2
31use crate::remote::types::{FetchResult, ImportResult, MirrorType, PushResult, RemoteConfig};
42
5/// Operation trait for remote/mirror operations.
63pub trait RemoteOps {
7 /// Clone/import a bare repository from a remote path.
8 fn remote_clone_import(
4 fn remote_clone_import(
95 &self,
106 src: &str,
117 config: &RemoteConfig,
128 ) -> Result<ImportResult, crate::Error>;
13 /// Fetch remote updates.
14 fn remote_fetch(&self, config: &RemoteConfig) -> Result<FetchResult, crate::Error>;
15 /// Push to a remote.
16 fn remote_push(
9 fn remote_fetch(&self, config: &RemoteConfig) -> Result<FetchResult, crate::Error>;
10 fn remote_push(
1711 &self,
1812 config: &RemoteConfig,
1913 refspec: Option<&str>,
2014 ) -> Result<PushResult, crate::Error>;
21 /// Set a mirror.
22 fn remote_set_mirror(
15 fn remote_set_mirror(
2316 &self,
2417 mirror_type: MirrorType,
2518 config: &RemoteConfig,
2619 ) -> Result<(), crate::Error>;
27 /// Remove a remote.
28 fn remote_remove_remote(&self, name: &str) -> Result<(), crate::Error>;
20 fn remote_remove_remote(&self, name: &str) -> Result<(), crate::Error>;
2921}
Modified lib/git/remote/types.rs View lib/git/remote/types.rs
@@ -1,10 +1,5 @@
1//! remote Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Authentication for mirror/remote configuration.
6///
7/// Credentials are redacted in `Debug` output and never serialized.
83#[derive(Clone)]
94pub enum RemoteAuth {
105 None,
@@ -56,7 +51,6 @@
5651 }
5752}
5853
59/// Remote configuration.
6054#[derive(Debug, Clone)]
6155pub struct RemoteConfig {
6256 pub url: String,
@@ -102,28 +96,24 @@
10296 }
10397}
10498
105/// Mirror type.
10699#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107100pub enum MirrorType {
108101 Pull,
109102 Push,
110103}
111104
112/// Import result.
113105#[derive(Debug, Clone, Serialize, Deserialize)]
114106pub struct ImportResult {
115107 pub default_branch: String,
116108 pub commit_count: u64,
117109}
118110
119/// Fetch result.
120111#[derive(Debug, Clone, Serialize, Deserialize)]
121112pub struct FetchResult {
122113 pub updated: Vec<String>,
123114 pub new: Vec<String>,
124115}
125116
126/// Push result.
127117#[derive(Debug, Clone, Serialize, Deserialize)]
128118pub struct PushResult {
129119 pub pushed: Vec<String>,
Modified lib/git/remote/usecase.rs View lib/git/remote/usecase.rs
@@ -1,5 +1,3 @@
1//! remote usecase: remote/mirror operations.
2
31use crate::GitRepository;
42use crate::remote::impls::RemoteOps;
53use crate::remote::types::{
@@ -14,12 +12,9 @@
1412 _config: &RemoteConfig,
1513 ) -> Result<ImportResult, crate::Error> {
1614 // SSRF / path-traversal guard: only allow http(s)/ssh remote URLs,
17 // never local paths or file://.
1815 validate::allowed_remote_scheme(src)?;
1916 let repo = self.repo()?;
2017 let path = repo.path().to_path_buf();
21 // Import into the current bare repository via `git fetch` from the
22 // remote (avoids a shell; src is a validated URL).
2318 crate::gitcli::run_remote(
2419 &path,
2520 &[
Modified lib/git/signature/impls.rs View lib/git/signature/impls.rs
@@ -1,21 +1,14 @@
1//! signature Operation method trait for the module.
2
31use gix::hash::ObjectId;
42
53use crate::signature::types::{SignatureInfo, VerifyResult};
64
7/// Operation trait for signature verification.
85pub trait SignatureOps {
9 /// Verify a commit signature.
10 fn signature_verify_commit(&self, id: &ObjectId) -> Result<VerifyResult, crate::Error>;
11 /// Verify a tag signature.
12 fn signature_verify_tag(&self, id: &ObjectId) -> Result<VerifyResult, crate::Error>;
13 /// Read a commit signature.
14 fn signature_commit_signature(
6 fn signature_verify_commit(&self, id: &ObjectId) -> Result<VerifyResult, crate::Error>;
7 fn signature_verify_tag(&self, id: &ObjectId) -> Result<VerifyResult, crate::Error>;
8 fn signature_commit_signature(
159 &self,
1610 id: &ObjectId,
1711 ) -> Result<Option<SignatureInfo>, crate::Error>;
18 /// Read a tag signature.
19 fn signature_tag_signature(&self, id: &ObjectId)
12 fn signature_tag_signature(&self, id: &ObjectId)
2013 -> Result<Option<SignatureInfo>, crate::Error>;
2114}
Modified lib/git/signature/types.rs View lib/git/signature/types.rs
@@ -1,8 +1,5 @@
1//! signature Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Signature verification result.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct VerifyResult {
85 pub signed: bool,
@@ -11,7 +8,6 @@
118 pub signer: Option<String>,
129}
1310
14/// Signature info.
1511#[derive(Debug, Clone, Serialize, Deserialize)]
1612pub struct SignatureInfo {
1713 pub signature: String,
Modified lib/git/signature/usecase.rs View lib/git/signature/usecase.rs
@@ -1,5 +1,3 @@
1//! signature usecase: signature verification.
2
31use gix::hash::ObjectId;
42
53use crate::GitRepository;
@@ -17,10 +15,6 @@
1715 None
1816}
1917
20/// Verify a PGP signature over the given signed data using the system `gpg`.
21///
22/// Returns `Ok(true)` if the signature is valid, `Ok(false)` if invalid/unsigned.
23/// Returns `Err` if gpg is unavailable (fail-closed so callers can decide).
2418fn verify_pgp(signature: &[u8], signed_data: &[u8]) -> Result<bool, crate::Error> {
2519 use std::io::Write;
2620 use std::process::{Command, Stdio};
@@ -38,7 +32,6 @@
3832 .stdin
3933 .take()
4034 .ok_or_else(|| crate::Error::Other("gpg stdin unavailable".to_string()))?;
41 // Write signature then signed data to gpg's stdin.
4235 stdin
4336 .write_all(signature)
4437 .map_err(|e| crate::Error::Io(e.to_string()))?;
@@ -59,8 +52,6 @@
5952impl SignatureOps for GitRepository {
6053 fn signature_verify_commit(&self, id: &ObjectId) -> Result<VerifyResult, crate::Error> {
6154 let repo = self.repo()?;
62 // Get the raw commit bytes so we can extract the PGP signature and
63 // the exact signed data.
6455 let raw = repo
6556 .find_object(*id)
6657 .map_err(|e| crate::Error::Find(e.to_string()))?
@@ -117,8 +108,6 @@
117108 signer: None,
118109 });
119110 };
120 // Reconstruct the signed tag payload (gix does not expose it directly,
121 // so verify the signature over the raw tag object bytes).
122111 let mut buf = Vec::new();
123112 use gix::objs::WriteTo;
124113 tag_ref
Modified lib/git/submodule/impls.rs View lib/git/submodule/impls.rs
@@ -1,15 +1,9 @@
1//! submodule Operation method trait for the module.
2
31use gix::hash::ObjectId;
42
53use crate::submodule::types::{LinkUrl, SubmoduleList};
64
7/// Operation trait for the submodule module.
85pub trait SubmoduleOps {
9 /// List submodules.
10 fn submodule_list(&self, tree_id: &ObjectId) -> Result<SubmoduleList, crate::Error>;
11 /// Resolve a submodule link URL.
12 fn submodule_link_url(&self, path: &str, tree_id: &ObjectId) -> Result<LinkUrl, crate::Error>;
13 /// Get a submodule commit.
14 fn submodule_resolve(&self, path: &str, tree_id: &ObjectId) -> Result<ObjectId, crate::Error>;
6 fn submodule_list(&self, tree_id: &ObjectId) -> Result<SubmoduleList, crate::Error>;
7 fn submodule_link_url(&self, path: &str, tree_id: &ObjectId) -> Result<LinkUrl, crate::Error>;
8 fn submodule_resolve(&self, path: &str, tree_id: &ObjectId) -> Result<ObjectId, crate::Error>;
159}
Modified lib/git/submodule/types.rs View lib/git/submodule/types.rs
@@ -1,8 +1,5 @@
1//! submodule Field types returned by the module.
2
31use serde::{Deserialize, Serialize};
42
5/// Submodule info.
63#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct Submodule {
85 pub path: String,
@@ -11,13 +8,11 @@
118 pub commit_sha: gix::hash::ObjectId,
129}
1310
14/// List of submodules.
1511#[derive(Debug, Clone, Serialize, Deserialize)]
1612pub struct SubmoduleList {
1713 pub submodules: Vec<Submodule>,
1814}
1915
20/// Link resolution result.
2116#[derive(Debug, Clone, Serialize, Deserialize)]
2217pub struct LinkUrl {
2318 pub path: String,
Modified lib/git/submodule/usecase.rs View lib/git/submodule/usecase.rs
@@ -1,5 +1,3 @@
1//! submodule usecase: list submodules from a tree.
2
31use gix::hash::ObjectId;
42use gix::objs::tree::EntryRef;
53
Modified lib/git/tag/impls.rs View lib/git/tag/impls.rs
@@ -1,34 +1,23 @@
1//! tag Operation method trait for the module.
2
31use gix::hash::ObjectId;
42
53use crate::tag::types::{CreateTagResult, Tag, TagList, TagType};
64
7/// Operation trait for the tag module.
85pub trait TagOps {
9 /// List tags.
10 fn tag_list(&self, page: u64, page_size: u64) -> Result<TagList, crate::Error>;
11 /// Get a tag.
12 fn tag_get(&self, name: &str) -> Result<Tag, crate::Error>;
13 /// Resolve a tag to a commit.
14 fn tag_resolve(&self, name: &str) -> Result<ObjectId, crate::Error>;
15 /// Create a lightweight tag.
16 fn tag_create_lightweight(
6 fn tag_list(&self, page: u64, page_size: u64) -> Result<TagList, crate::Error>;
7 fn tag_get(&self, name: &str) -> Result<Tag, crate::Error>;
8 fn tag_resolve(&self, name: &str) -> Result<ObjectId, crate::Error>;
9 fn tag_create_lightweight(
1710 &self,
1811 name: &str,
1912 revision: &str,
2013 ) -> Result<CreateTagResult, crate::Error>;
21 /// Create an annotated tag.
22 fn tag_create_annotated(
14 fn tag_create_annotated(
2315 &self,
2416 name: &str,
2517 message: &str,
2618 revision: &str,
2719 ) -> Result<CreateTagResult, crate::Error>;
28 /// Delete a tag.
29 fn tag_delete(&self, name: &str) -> Result<(), crate::Error>;
30 /// Tag type.
31 fn tag_type(&self, name: &str) -> Result<TagType, crate::Error>;
32 /// Whether a tag is protected.
33 fn tag_is_tag_protected(&self, name: &str) -> Result<bool, crate::Error>;
20 fn tag_delete(&self, name: &str) -> Result<(), crate::Error>;
21 fn tag_type(&self, name: &str) -> Result<TagType, crate::Error>;
22 fn tag_is_tag_protected(&self, name: &str) -> Result<bool, crate::Error>;
3423}
Modified lib/git/tag/types.rs View lib/git/tag/types.rs
@@ -1,38 +1,31 @@
1//! tag Field types returned by the tag module (aligned with Gitea modules/git/tag.go).
2
31use serde::{Deserialize, Serialize};
42
53use crate::commit::types::Signature;
64use crate::object::types::ObjectType;
75
8/// Tag type.
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107pub enum TagType {
118 Lightweight,
129 Annotated,
1310}
1411
15/// Tag info.
1612#[derive(Debug, Clone, Serialize, Deserialize)]
1713pub struct Tag {
1814 pub name: String,
1915 pub id: gix::hash::ObjectId,
20 /// Object id the reference points to (after peeling).
21 pub object: gix::hash::ObjectId,
16 pub object: gix::hash::ObjectId,
2217 pub tag_type: TagType,
2318 pub message: Option<String>,
2419 pub tagger: Option<Signature>,
2520 pub signature: Option<Signature>,
2621}
2722
28/// List of tags.
2923#[derive(Debug, Clone, Serialize, Deserialize)]
3024pub struct TagList {
3125 pub tags: Vec<Tag>,
3226 pub total: u64,
3327}
3428
35/// Create-tag result.
3629#[derive(Debug, Clone, Serialize, Deserialize)]
3730pub struct CreateTagResult {
3831 pub name: String,
Modified lib/git/tag/usecase.rs View lib/git/tag/usecase.rs
@@ -1,5 +1,3 @@
1//! tag Use Case: implementation of `TagOps` for `GitRepository`.
2
31use gix::hash::ObjectId;
42use gix::objs::Kind as GixKind;
53use gix::objs::WriteTo;
Modified lib/git/tree/impls.rs View lib/git/tree/impls.rs
@@ -1,19 +1,11 @@
1//! tree Operation trait for the tree module (tree access on bare repositories based on gix).
2
31use gix::hash::ObjectId;
42
53use crate::tree::types::{EntryFollowResult, PathLastUpdated, PathResult, Tree, TreeEntry};
64
7/// Operation trait for the tree module.
85pub trait TreeOps {
9 /// Read a tree.
10 fn tree_read(&self, id: &ObjectId) -> Result<Tree, crate::Error>;
11 /// Resolve a tree entry by path.
12 fn tree_resolve_path(&self, tree_id: &ObjectId, path: &str) -> Result<TreeEntry, crate::Error>;
13 /// Walk a tree (recursive).
14 fn tree_walk(&self, tree_id: &ObjectId) -> Result<Vec<PathResult>, crate::Error>;
15 /// Follow a symbolic link.
16 fn tree_follow(&self, entry: &TreeEntry) -> Result<EntryFollowResult, crate::Error>;
17 /// Last-update time for every tree/blob in the given commit's tree.
18 fn tree_last_updated(&self, rev: &str) -> Result<Vec<PathLastUpdated>, crate::Error>;
6 fn tree_read(&self, id: &ObjectId) -> Result<Tree, crate::Error>;
7 fn tree_resolve_path(&self, tree_id: &ObjectId, path: &str) -> Result<TreeEntry, crate::Error>;
8 fn tree_walk(&self, tree_id: &ObjectId) -> Result<Vec<PathResult>, crate::Error>;
9 fn tree_follow(&self, entry: &TreeEntry) -> Result<EntryFollowResult, crate::Error>;
10 fn tree_last_updated(&self, rev: &str) -> Result<Vec<PathLastUpdated>, crate::Error>;
1911}
Modified lib/git/tree/types.rs View lib/git/tree/types.rs
@@ -1,10 +1,7 @@
1//! tree Field types returned by the tree module (aligned with Gitea modules/git/tree_entry.go and tree.go).
2
31use serde::{Deserialize, Serialize};
42
53use crate::object::types::ObjectType;
64
7/// Tree entry mode.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
96pub struct EntryMode(pub u32);
107
@@ -41,7 +38,6 @@
4138 }
4239}
4340
44/// A tree entry.
4541#[derive(Debug, Clone, Serialize, Deserialize)]
4642pub struct TreeEntry {
4743 pub id: gix::hash::ObjectId,
@@ -51,14 +47,12 @@
5147 pub object_type: ObjectType,
5248}
5349
54/// A tree.
5550#[derive(Debug, Clone, Serialize, Deserialize)]
5651pub struct Tree {
5752 pub id: gix::hash::ObjectId,
5853 pub entries: Vec<TreeEntry>,
5954}
6055
61/// Symbolic link follow result.
6256#[derive(Debug, Clone, Serialize, Deserialize)]
6357pub struct EntryFollowResult {
6458 pub symlink_content: Option<String>,
@@ -66,20 +60,16 @@
6660 pub target_entry: Option<TreeEntry>,
6761}
6862
69/// Path resolution result.
7063#[derive(Debug, Clone, Serialize, Deserialize)]
7164pub struct PathResult {
7265 pub entry: TreeEntry,
7366 pub path: String,
7467}
7568
76/// Last-update info for a tree/blob path within a commit's tree.
7769#[derive(Debug, Clone, Serialize, Deserialize)]
7870pub struct PathLastUpdated {
7971 pub path: String,
8072 pub object_type: ObjectType,
81 /// Unix timestamp (seconds) of the last commit that touched this path.
82 pub last_updated: i64,
83 /// ObjectId of the last commit that touched this path.
84 pub commit_id: gix::hash::ObjectId,
73 pub last_updated: i64,
74 pub commit_id: gix::hash::ObjectId,
8575}
Modified lib/git/tree/usecase.rs View lib/git/tree/usecase.rs
@@ -1,5 +1,3 @@
1//! tree usecase: impl TreeOps for GitRepository.
2
31use crate::GitRepository;
42use crate::object::types::ObjectType;
53use crate::tree::impls::TreeOps;
@@ -183,8 +181,6 @@
183181 let commit_time = decoded.committer.time.seconds;
184182 let tree_id = decoded.tree;
185183
186 // Single pass over history: path -> (last commit time, last commit id)
187 // (newest commit wins, since git log is newest-first).
188184 let repo_path = repo.path();
189185 let log = crate::gitcli::run(
190186 repo_path,
@@ -200,7 +196,7 @@
200196 std::collections::HashMap::new();
201197 let mut cur_hash: Option<gix::hash::ObjectId> = None;
202198 let mut cur_time: Option<i64> = None;
203 let mut stage = 0u8; // 0=hash, 1=time, 2=files
199 let mut stage = 0u8;
204200 for line in log.lines() {
205201 let line = line.trim();
206202 if line.is_empty() {
@@ -242,8 +238,6 @@
242238 }
243239}
244240
245// Recursively walk a tree, emitting each path's last-update info.
246// For directories, the last-update time/id is the max over its descendants.
247241fn walk_last_updated(
248242 repo: &gix::Repository,
249243 tree_id: gix::ObjectId,
Modified lib/git/error.rs View lib/git/error.rs
@@ -1,6 +1,3 @@
1//! Unified error type, used by the impls.rs of every module.
2
3/// The unified error type.
41#[derive(Debug, thiserror::Error)]
52pub enum Error {
63 #[error("git object does not exist: {0}")]
Modified lib/git/gitcli.rs View lib/git/gitcli.rs
@@ -1,16 +1,8 @@
1//! gitcli: run the system `git` via `duct` for operations gix does not expose
2//! (gc, prune, pack, patch apply, archive, remote fetch/push/clone).
3//!
4//! All invocations are scoped to the repository's `--git-dir` and never use a
5//! shell, so there is no shell-injection surface. Arguments are passed as
6//! separate argv elements.
7
81use std::path::Path;
92
103use crate::Error;
114use crate::remote::types::RemoteAuth;
125
13/// Run `git` with the given args against `repo_path`, returning stdout.
146pub fn run(repo_path: &Path, args: &[&str]) -> Result<String, Error> {
157 let out = duct::cmd("git", args)
168 .env("GIT_DIR", repo_path)
@@ -20,8 +12,6 @@
2012 Ok(out)
2113}
2214
23/// Run `git` with the given args against `repo_path`, returning raw bytes
24/// (binary-safe, e.g. for `git archive`).
2515pub fn run_bytes(repo_path: &Path, args: &[&str]) -> Result<Vec<u8>, Error> {
2616 let out = duct::cmd("git", args)
2717 .env("GIT_DIR", repo_path)
@@ -39,7 +29,6 @@
3929 Ok(out.stdout)
4030}
4131
42/// Run `git` with the given args against `repo_path`, returning exit status.
4332pub fn run_status(repo_path: &Path, args: &[&str]) -> Result<bool, Error> {
4433 let output = duct::cmd("git", args)
4534 .env("GIT_DIR", repo_path)
@@ -49,11 +38,6 @@
4938 Ok(output.status.success())
5039}
5140
52/// Run a remote `git` command (fetch/push/clone) with the given auth.
53///
54/// Token auth is supplied via `GIT_ASKPASS` pointing to a temp helper that
55/// writes the token to stdout, so the token never appears in argv or in the
56/// URL. The helper is removed afterwards.
5741pub fn run_remote(repo_path: &Path, args: &[&str], auth: &RemoteAuth) -> Result<String, Error> {
5842 let mut cmd = duct::cmd("git", args)
5943 .env("GIT_DIR", repo_path)
@@ -79,7 +63,6 @@
7963 Ok(out)
8064}
8165
82/// A temporary GIT_ASKPASS helper on disk.
8366struct AskPassHelper {
8467 path: std::path::PathBuf,
8568}
@@ -90,7 +73,6 @@
9073 }
9174}
9275
93/// Write a temp script that echoes the token when git prompts for credentials.
9476fn write_askpass_helper(token: &str) -> Result<AskPassHelper, Error> {
9577 let dir = std::env::temp_dir();
9678 let path = dir.join(format!("git-askpass-{}.sh", std::process::id()));
Modified lib/git/policy.rs View lib/git/policy.rs
@@ -1,28 +1,12 @@
1//! policy: external protection-policy callback for refs and tags.
2//!
3//! The default policy protects common integration branches. Callers that host
4//! this library can inject their own `PolicyProvider` via
5//! [`GitRepository::with_policy`](crate::GitRepository::with_policy) to enforce
6//! real repository-specific protection rules.
7
81use std::sync::Arc;
92
10/// A protection policy consulted before mutating refs/tags.
11///
12/// Implementations must be cheap, pure, and side-effect free. They are called
133/// on every ref/tag mutation to decide whether the operation is allowed.
144pub trait PolicyProvider: Send + Sync {
15 /// Whether the given branch (short name, e.g. `main`) is protected.
16 fn is_branch_protected(&self, branch: &str) -> bool;
17 /// Whether the given tag (short name) is protected.
18 fn is_tag_protected(&self, tag: &str) -> bool;
5 fn is_branch_protected(&self, branch: &str) -> bool;
6 fn is_tag_protected(&self, tag: &str) -> bool;
197}
208
21/// Default policy: protects common integration branches, nothing else.
22///
23/// This is a safe default for a git-hosting backend: it blocks force-push and
249/// deletion of `main`/`master`/`develop` and `release/*` tags, while allowing
25/// anything else. Callers can replace it with their own provider.
2610#[derive(Debug, Clone, Default)]
2711pub struct DefaultPolicy;
2812
@@ -42,7 +26,6 @@
4226 }
4327}
4428
45/// A policy that protects nothing (used by tests / callers that opt out).
4629#[derive(Debug, Clone, Default)]
4730pub struct NoProtection;
4831
@@ -55,10 +38,8 @@
5538 }
5639}
5740
58/// Convenience alias for the boxed policy held by a repository.
5941pub type Policy = Arc<dyn PolicyProvider>;
6042
61/// Build an [`Arc<dyn PolicyProvider>`] from a concrete provider.
6243pub fn arc<P: PolicyProvider + 'static>(provider: P) -> Policy {
6344 Arc::new(provider)
6445}
Modified lib/git/usecase_cache.rs View lib/git/usecase_cache.rs
@@ -1,15 +1,3 @@
1//! Cache helpers for `lib/git/*/usecase.rs` trait methods.
2//!
3//! Trait methods are sync but the underlying `Cache` is async. These helpers
4//! wrap the sync cache bridge (`ObjectCache::get_sync` / `set_sync` /
5//! `object_pool_*_sync`) and add the key conventions used across the crate:
6//!
7//! - Content-addressed: `object.pool.{kind}.{id}` — TTL = 0 (immutable).
8//! - Reference-derived: `{module}.{func}.{key_part}:{tip_oid}` — TTL = 60s.
9//!
10//! Helpers in this file must not panic on transient cache failures. They are
11//! best-effort: a missing or malformed cache entry simply means a cache miss.
12
131use gix::hash::ObjectId;
142use serde::Serialize;
153use serde::de::DeserializeOwned;
@@ -17,24 +5,19 @@
175use crate::GitRepository;
186use crate::error::Error;
197
20/// TTL for content-addressed keys (immutable — set to 0 = no expiry).
218pub const IMMUTABLE_TTL: u64 = 0;
229
23/// TTL for reference-derived keys (depend on tip / branch state).
2410pub const DERIVED_TTL: u64 = 60;
2511
2612impl GitRepository {
27 /// Resolve HEAD to a detached `ObjectId`. Used as the invalidation anchor
28 /// for all reference-derived cache entries.
29 pub(crate) fn tip_id(&self) -> Result<ObjectId, Error> {
13 pub(crate) fn tip_id(&self) -> Result<ObjectId, Error> {
3014 let repo = self.repo()?;
3115 repo.head_id()
3216 .map(|id| id.detach())
3317 .map_err(|e| Error::Ref(e.to_string()))
3418 }
3519
36 /// Look up a content-addressed entry (`object.pool.{kind}.{id}`).
37 pub(crate) fn cache_get_oid<T>(&self, kind: &str, id: &ObjectId) -> Option<T>
20 pub(crate) fn cache_get_oid<T>(&self, kind: &str, id: &ObjectId) -> Option<T>
3821 where
3922 T: DeserializeOwned + Send + Sync,
4023 {
@@ -42,14 +25,12 @@
4225 self.cache.object_pool_get_sync(&k)
4326 }
4427
45 /// Store a content-addressed entry with TTL=0 (immutable).
46 pub(crate) fn cache_put_oid<T: Serialize>(&self, kind: &str, id: &ObjectId, v: &T) {
28 pub(crate) fn cache_put_oid<T: Serialize>(&self, kind: &str, id: &ObjectId, v: &T) {
4729 let k = format!("{kind}.{id}");
4830 self.cache.set_sync_with_ttl(&k, v, IMMUTABLE_TTL);
4931 }
5032
51 /// Look up a reference-derived entry (`{module}.{func}.{key_part}:{tip_oid}`).
52 pub(crate) fn cache_get_rev<T>(
33 pub(crate) fn cache_get_rev<T>(
5334 &self,
5435 module: &str,
5536 func: &str,
@@ -63,8 +44,7 @@
6344 self.cache.get_sync(&k)
6445 }
6546
66 /// Store a reference-derived entry with TTL=60s.
67 pub(crate) fn cache_put_rev<T: Serialize>(
47 pub(crate) fn cache_put_rev<T: Serialize>(
6848 &self,
6949 module: &str,
7050 func: &str,
Modified lib/git/validate.rs View lib/git/validate.rs
@@ -1,13 +1,7 @@
1//! validate: common input-validation helpers shared across the crate.
2
3/// RFC 3986 hex-digit check for a single byte.
41fn is_hex(b: u8) -> bool {
52 b.is_ascii_hexdigit()
63}
74
8/// Validate a git object id string (hex) of the given byte length.
9///
10/// Returns `Ok(())` if `s` is exactly `len` hexadecimal characters.
115pub fn validate_oid(s: &str, len: u64) -> Result<(), crate::Error> {
126 if s.len() as u64 != len || !s.bytes().all(is_hex) {
137 return Err(crate::Error::InvalidRevSpec(format!(
@@ -17,11 +11,6 @@
1711 Ok(())
1812}
1913
20/// Validate a short name (branch/tag) before it is interpolated into a ref path.
21///
22/// Rejects empty names, path separators, `..`, leading `/`, whitespace and
23/// control characters. An already-qualified `refs/...` name is rejected here;
24/// callers that accept fully-qualified names must validate separately.
2514pub fn validate_short_name(kind: &str, name: &str) -> Result<(), crate::Error> {
2615 if name.is_empty() {
2716 return Err(crate::Error::InvalidRefName(format!("empty {kind} name")));
@@ -47,7 +36,6 @@
4736 Ok(())
4837}
4938
50/// Validate a fully-qualified ref name (start with `refs/`).
5139pub fn validate_qualified_ref(name: &str) -> Result<(), crate::Error> {
5240 if !name.starts_with("refs/") {
5341 return Err(crate::Error::InvalidRefName(format!(
@@ -64,7 +52,6 @@
6452
6553/// Validate a remote URL scheme; returns the scheme if allowed.
6654pub fn allowed_remote_scheme(url: &str) -> Result<(), crate::Error> {
67 // Reject local/absolute paths and file:// — only explicit http(s) or ssh
6855 // are allowed for remote operations.
6956 let scheme = url
7057 .split_once("://")
@@ -78,13 +65,10 @@
7865 }
7966}
8067
81/// Maximum size (bytes) for a single object written to the object database.
82pub const MAX_OBJECT_SIZE: u64 = 100 * 1024 * 1024; // 100 MiB
68pub const MAX_OBJECT_SIZE: u64 = 100 * 1024 * 1024;
8369
84/// Maximum size (bytes) for a blob read into memory.
85pub const MAX_BLOB_SIZE: u64 = 100 * 1024 * 1024; // 100 MiB
70pub const MAX_BLOB_SIZE: u64 = 100 * 1024 * 1024;
8671
87/// Enforce a maximum object size.
8872pub fn enforce_max_size(data: &[u8], max: u64, what: &str) -> Result<(), crate::Error> {
8973 if data.len() as u64 > max {
9074 return Err(crate::Error::Other(format!(