list_virtual_branches now includes the series data

at this point only local patches are returned
This commit is contained in:
Kiril Videlov 2024-10-02 23:49:58 +02:00
parent 99e48e4f3e
commit e00f47aea5
4 changed files with 59 additions and 13 deletions

View File

@ -28,7 +28,7 @@ use gitbutler_repo::{
rebase::{cherry_rebase, cherry_rebase_group},
LogUntil, RepoActionsExt, RepositoryExt,
};
use gitbutler_stack::Stack;
use gitbutler_stack::{commit_by_oid_or_change_id, Stack};
use gitbutler_time::time::now_since_unix_epoch_ms;
use serde::Serialize;
use std::collections::HashSet;
@ -417,6 +417,14 @@ pub fn list_virtual_branches_cached(
let refname = branch.refname()?.into();
// TODO: Error out here once this API is stable
let series = match stack_series(ctx, &branch, &default_target, &check_commit) {
Ok(series) => series,
Err(e) => {
tracing::warn!("failed to compute stack series: {:?}", e);
vec![]
}
};
let branch = VirtualBranch {
id: branch.id,
name: branch.name,
@ -441,7 +449,7 @@ pub fn list_virtual_branches_cached(
fork_point,
refname,
tree: branch.tree,
series: vec![],
series,
};
branches.push(branch);
}
@ -453,6 +461,42 @@ pub fn list_virtual_branches_cached(
Ok((branches, status.skipped_files))
}
fn stack_series(
ctx: &CommandContext,
branch: &Branch,
default_target: &Target,
check_commit: &IsCommitIntegrated,
) -> Result<Vec<PatchSeries>> {
let mut api_series: Vec<PatchSeries> = vec![];
for series in branch.list_series(ctx)? {
let upstream_reference = default_target.push_remote_name.as_ref().and_then(|remote| {
if series.head.pushed(remote.as_str(), ctx).ok()? {
series.head.remote_reference(remote.as_str()).ok()
} else {
None
}
});
let mut patches: Vec<VirtualBranchCommit> = vec![];
for patch in series.local_commits {
let commit = commit_by_oid_or_change_id(&patch, ctx, branch.head, default_target)?;
let is_integrated = check_commit.is_integrated(&commit)?;
// TODO: correctly determine if commit is remote
let vcommit =
commit_to_vbranch_commit(ctx, branch, &commit, is_integrated, false, None)?;
patches.push(vcommit);
}
api_series.push(PatchSeries {
name: series.head.name,
description: series.head.description,
upstream_reference,
patches,
upstream_patches: vec![],
});
}
Ok(api_series)
}
/// The commit-data we can use for comparison to see which remote-commit was used to craete
/// a local commit from.
/// Note that trees can't be used for comparison as these are typically rebased.

View File

@ -39,12 +39,12 @@ impl Display for CommitOrChangeId {
impl PatchReference {
/// Returns a fully qualified reference with the supplied remote e.g. `refs/remotes/origin/base-branch-improvements`
pub fn remote_reference(&self, remote: String) -> Result<String> {
pub fn remote_reference(&self, remote: &str) -> Result<String> {
Ok(format!("refs/remotes/{}/{}", remote, self.name))
}
/// Returns `true` if the reference is pushed to the provided remote
pub fn pushed(&self, remote: String, ctx: CommandContext) -> Result<bool> {
pub fn pushed(&self, remote: &str, ctx: &CommandContext) -> Result<bool> {
let remote_ref = self.remote_reference(remote)?;
Ok(ctx.repository().find_reference(&remote_ref).is_ok())
}

View File

@ -2,4 +2,4 @@ mod heads;
mod series;
mod stack;
pub use series::Series;
pub use stack::{PatchReferenceUpdate, Stack, TargetUpdate};
pub use stack::{commit_by_oid_or_change_id, PatchReferenceUpdate, Stack, TargetUpdate};

View File

@ -266,7 +266,7 @@ impl Stack for Branch {
// ensure that the head has not been pushed to a remote yet
let default_target = branch_state(ctx).get_default_target()?;
if let Some(remote) = default_target.push_remote_name {
if reference_exists(ctx, &head.remote_reference(remote)?)? {
if reference_exists(ctx, &head.remote_reference(remote.as_str())?)? {
bail!("Cannot update the name of a head that has been pushed to a remote");
}
}
@ -297,15 +297,17 @@ impl Stack for Branch {
}
let (_, reference) = get_head(&self.heads, &branch_name)?;
let default_target = branch_state(ctx).get_default_target()?;
let commit = get_target_commit(&reference.target, ctx, self.head, &default_target)?;
let commit =
commit_by_oid_or_change_id(&reference.target, ctx, self.head, &default_target)?;
let remote_name = branch_state(ctx)
.get_default_target()?
.push_remote_name
.ok_or(anyhow!(
"No remote has been configured for the target branch"
))?;
let upstream_refname = RemoteRefname::from_str(&reference.remote_reference(remote_name)?)
.context("Failed to parse the remote reference for branch")?;
let upstream_refname =
RemoteRefname::from_str(&reference.remote_reference(remote_name.as_str())?)
.context("Failed to parse the remote reference for branch")?;
ctx.push(
commit.id(),
&upstream_refname,
@ -327,7 +329,7 @@ impl Stack for Branch {
let mut previous_head = repo.merge_base(self.head, default_target.sha)?;
for head in self.heads.clone() {
let head_commit =
get_target_commit(&head.target, ctx, self.head, &default_target)?.id();
commit_by_oid_or_change_id(&head.target, ctx, self.head, &default_target)?.id();
let local_patches = repo
.log(head_commit, LogUntil::Commit(previous_head))?
.iter()
@ -361,7 +363,7 @@ fn validate_target(
state: &VirtualBranchesHandle,
) -> Result<()> {
let default_target = state.get_default_target()?;
let commit = get_target_commit(&reference.target, ctx, stack_head, &default_target)?;
let commit = commit_by_oid_or_change_id(&reference.target, ctx, stack_head, &default_target)?;
let stack_commits = ctx
.repository()
// TODO: seems like the range that is actually needed is from head to the merge base
@ -440,7 +442,7 @@ fn validate_name(
let default_target = state.get_default_target()?;
// assert that there is no remote git reference with this name
if let Some(remote_name) = default_target.push_remote_name {
if reference_exists(ctx, &reference.remote_reference(remote_name)?)? {
if reference_exists(ctx, &reference.remote_reference(remote_name.as_str())?)? {
return Err(anyhow!(
"A git reference with the name {} exists",
&reference.name
@ -486,7 +488,7 @@ fn branch_state(ctx: &CommandContext) -> VirtualBranchesHandle {
VirtualBranchesHandle::new(ctx.project().gb_dir())
}
fn get_target_commit<'a>(
pub fn commit_by_oid_or_change_id<'a>(
reference_target: &'a CommitOrChangeId,
ctx: &'a CommandContext,
stack_head: git2::Oid,