diff --git a/giterated-daemon/src/backend/git.rs b/giterated-daemon/src/backend/git.rs index 36c3dbe..25d7bed 100644 --- a/giterated-daemon/src/backend/git.rs +++ b/giterated-daemon/src/backend/git.rs @@ -13,7 +13,7 @@ use giterated_models::repository::{ RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositoryLastCommitOfFileRequest, RepositoryObjectType, RepositoryStatistics, - RepositoryStatisticsRequest, RepositoryTreeEntry, RepositoryVisibility, Visibility, + RepositoryStatisticsRequest, RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryBranchFilter, }; use giterated_models::user::User; @@ -771,33 +771,63 @@ impl RepositoryBackend for GitBackend { &mut self, requester: &Option, repository: &Repository, - _request: &RepositoryBranchesRequest, + request: &RepositoryBranchesRequest, ) -> Result, Error> { let git = self .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) .await?; - let mut branches = vec![]; - - for branch in git.branches(None)? { - let branch = branch?; + // Could be done better with the RepositoryBranchFilter::None check done beforehand. + let mut filtered_branches = git.branches(None)?.filter_map(|branch| { + let branch = branch.ok()?.0; - let Some(name) = branch.0.name().ok().flatten() else { - continue; + let Some(name) = branch.name().ok().flatten() else { + return None; }; // TODO: Non UTF-8? - let commit = - GitBackend::get_last_commit_in_rev(&git, branch.0.get().name().unwrap()).ok(); + let Some(commit) = GitBackend::get_last_commit_in_rev(&git, branch.get().name().unwrap()).ok() else { + return None; + }; // TODO: Implement stale with configurable age let stale = false; - branches.push(RepositoryBranch { - name: name.to_string(), - stale, - last_commit: commit, - }) + // Filter based on if the branch is stale or not + if request.filter != RepositoryBranchFilter::None { + #[allow(clippy::if_same_then_else)] + if stale && request.filter == RepositoryBranchFilter::Active { + return None; + } else if !stale && request.filter == RepositoryBranchFilter::Stale { + return None; + } + } + + Some((name.to_string(), branch, stale, commit)) + }).collect::>(); + + // Sort the branches by commit date + filtered_branches.sort_by(|(_, _, _, c1), (_, _, _, c2)| c2.time.cmp(&c1.time)); + // Go to the requested position + let mut filtered_branches = filtered_branches.iter().skip(request.range.0); + + let head = git.head()?; + let mut branches = vec![]; + + // Iterate through the filtered branches using the passed range + for _ in request.range.0..request.range.1 { + let Some((name, branch, stale, commit)) = filtered_branches.next() else { + break; + }; + + // Get how many commits are ahead of and behind of the head + let ahead_behind_head = if head.target().is_some() && branch.get().target().is_some() { + git.graph_ahead_behind(branch.get().target().unwrap(), head.target().unwrap()).ok() + } else { + None + }; + + branches.push(RepositoryBranch { name: name.to_string(), stale: *stale, last_commit: Some(commit.clone()), ahead_behind_head }) } Ok(branches) diff --git a/giterated-models/src/repository/mod.rs b/giterated-models/src/repository/mod.rs index cd5f87b..6ab085f 100644 --- a/giterated-models/src/repository/mod.rs +++ b/giterated-models/src/repository/mod.rs @@ -163,6 +163,16 @@ pub struct RepositoryBranch { pub stale: bool, /// The last commit made to the branch pub last_commit: Option, + /// How many commits this branch is ahead or behind from the main branch + pub ahead_behind_head: Option<(usize, usize)>, +} + +/// Filter primarily used for branch requests +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum RepositoryBranchFilter { + None, + Active, + Stale, } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/giterated-models/src/repository/operations.rs b/giterated-models/src/repository/operations.rs index 134e052..15c4631 100644 --- a/giterated-models/src/repository/operations.rs +++ b/giterated-models/src/repository/operations.rs @@ -9,7 +9,7 @@ use crate::{ use super::{ Commit, IssueLabel, Repository, RepositoryBranch, RepositoryDiff, RepositoryFile, - RepositoryIssue, RepositoryStatistics, RepositoryTreeEntry, RepositoryView, + RepositoryIssue, RepositoryStatistics, RepositoryTreeEntry, RepositoryView, RepositoryBranchFilter, }; /// A request to get a repository's information. @@ -264,7 +264,10 @@ impl GiteratedOperation for RepositoryStatisticsRequest { /// - User Authorization /// - Potential User permissions checks #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RepositoryBranchesRequest; +pub struct RepositoryBranchesRequest { + pub filter: RepositoryBranchFilter, + pub range: (usize, usize), +} impl GiteratedOperation for RepositoryBranchesRequest { type Success = Vec; @@ -392,9 +395,15 @@ impl + std::fmt::Debug> Object<'_, S pub async fn branches( &mut self, + filter: RepositoryBranchFilter, + range_start: usize, + range_end: usize, operation_state: &S, ) -> Result, OperationError> { - self.request::(RepositoryBranchesRequest, operation_state) + self.request::(RepositoryBranchesRequest { + filter, + range: (range_start, range_end), + }, operation_state) .await }