mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-18 14:31:30 +03:00
Merge pull request #5192 from gitbutlerapp/refactor-branch-actions-stack-module
Move a series converion function in branch-actions to separate module
This commit is contained in:
commit
2dc86e85bd
@ -1,12 +1,20 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use gitbutler_command_context::CommandContext;
|
||||||
use gitbutler_commit::commit_ext::CommitExt;
|
use gitbutler_commit::commit_ext::CommitExt;
|
||||||
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
use gitbutler_patch_reference::{CommitOrChangeId, PatchReference};
|
||||||
use gitbutler_project::Project;
|
use gitbutler_project::Project;
|
||||||
use gitbutler_stack::StackId;
|
use gitbutler_stack::{Stack, StackId, Target};
|
||||||
use gitbutler_stack_api::{PatchReferenceUpdate, StackExt};
|
use gitbutler_stack_api::{commit_by_oid_or_change_id, PatchReferenceUpdate, StackExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{actions::open_with_verify, VirtualBranchesExt};
|
use crate::{
|
||||||
|
actions::open_with_verify,
|
||||||
|
commit::{commit_to_vbranch_commit, VirtualBranchCommit},
|
||||||
|
r#virtual::{CommitData, IsCommitIntegrated, PatchSeries},
|
||||||
|
VirtualBranchesExt,
|
||||||
|
};
|
||||||
use gitbutler_operating_modes::assure_open_workspace_mode;
|
use gitbutler_operating_modes::assure_open_workspace_mode;
|
||||||
|
|
||||||
/// Adds a new "series/branch" to the Stack.
|
/// Adds a new "series/branch" to the Stack.
|
||||||
@ -139,3 +147,110 @@ pub fn push_stack(project: &Project, branch_id: StackId, with_force: bool) -> Re
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the stack series for the API.
|
||||||
|
/// Newest first, oldest last in the list
|
||||||
|
pub(crate) fn stack_series(
|
||||||
|
ctx: &CommandContext,
|
||||||
|
branch: &mut Stack,
|
||||||
|
default_target: &Target,
|
||||||
|
check_commit: &IsCommitIntegrated,
|
||||||
|
remote_commit_data: HashMap<CommitData, git2::Oid>,
|
||||||
|
) -> Result<(Vec<PatchSeries>, bool)> {
|
||||||
|
let mut requires_force = false;
|
||||||
|
let mut api_series: Vec<PatchSeries> = vec![];
|
||||||
|
let stack_series = branch.list_series(ctx)?;
|
||||||
|
let merge_base = ctx
|
||||||
|
.repository()
|
||||||
|
.merge_base(branch.head(), default_target.sha)?;
|
||||||
|
for series in stack_series.clone() {
|
||||||
|
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.clone().local_commits {
|
||||||
|
let commit =
|
||||||
|
commit_by_oid_or_change_id(&patch, ctx.repository(), branch.head(), merge_base)?;
|
||||||
|
let is_integrated = check_commit.is_integrated(&commit)?;
|
||||||
|
let copied_from_remote_id = CommitData::try_from(&commit)
|
||||||
|
.ok()
|
||||||
|
.and_then(|data| remote_commit_data.get(&data).copied());
|
||||||
|
let remote_commit_id = commit
|
||||||
|
.change_id()
|
||||||
|
.and_then(|change_id| {
|
||||||
|
series
|
||||||
|
.remote_commit_ids_by_change_id
|
||||||
|
.get(&change_id)
|
||||||
|
.cloned()
|
||||||
|
})
|
||||||
|
.or(copied_from_remote_id)
|
||||||
|
.or(if series.remote(&patch) {
|
||||||
|
Some(commit.id())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
});
|
||||||
|
if remote_commit_id.map_or(false, |id| commit.id() != id) {
|
||||||
|
requires_force = true;
|
||||||
|
}
|
||||||
|
let vcommit = commit_to_vbranch_commit(
|
||||||
|
ctx,
|
||||||
|
branch,
|
||||||
|
&commit,
|
||||||
|
is_integrated,
|
||||||
|
series.remote(&patch),
|
||||||
|
copied_from_remote_id,
|
||||||
|
remote_commit_id,
|
||||||
|
)?;
|
||||||
|
patches.push(vcommit);
|
||||||
|
}
|
||||||
|
patches.reverse();
|
||||||
|
let mut upstream_patches = vec![];
|
||||||
|
if let Some(upstream_reference) = upstream_reference.clone() {
|
||||||
|
let remote_head = ctx
|
||||||
|
.repository()
|
||||||
|
.find_reference(&upstream_reference)?
|
||||||
|
.peel_to_commit()?;
|
||||||
|
for patch in series.upstream_only_commits {
|
||||||
|
if let Ok(commit) = commit_by_oid_or_change_id(
|
||||||
|
&patch,
|
||||||
|
ctx.repository(),
|
||||||
|
remote_head.id(),
|
||||||
|
merge_base,
|
||||||
|
) {
|
||||||
|
let is_integrated = check_commit.is_integrated(&commit)?;
|
||||||
|
let vcommit = commit_to_vbranch_commit(
|
||||||
|
ctx,
|
||||||
|
branch,
|
||||||
|
&commit,
|
||||||
|
is_integrated,
|
||||||
|
true, // per definition
|
||||||
|
None, // per definition
|
||||||
|
Some(commit.id()),
|
||||||
|
)?;
|
||||||
|
upstream_patches.push(vcommit);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
upstream_patches.reverse();
|
||||||
|
api_series.push(PatchSeries {
|
||||||
|
name: series.head.name,
|
||||||
|
description: series.head.description,
|
||||||
|
upstream_reference,
|
||||||
|
patches,
|
||||||
|
upstream_patches,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
api_series.reverse();
|
||||||
|
|
||||||
|
// This is done for compatibility with the legacy flow.
|
||||||
|
// After a couple of weeks we can get rid of this.
|
||||||
|
if let Err(e) = branch.set_legacy_compatible_stack_reference(ctx) {
|
||||||
|
tracing::warn!("failed to set legacy compatible stack reference: {:?}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((api_series, requires_force))
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
hunk::VirtualBranchHunk,
|
hunk::VirtualBranchHunk,
|
||||||
integration::get_workspace_head,
|
integration::get_workspace_head,
|
||||||
remote::{branch_to_remote_branch, RemoteBranch},
|
remote::{branch_to_remote_branch, RemoteBranch},
|
||||||
|
stack::stack_series,
|
||||||
status::{get_applied_status, get_applied_status_cached},
|
status::{get_applied_status, get_applied_status_cached},
|
||||||
Get, VirtualBranchesExt,
|
Get, VirtualBranchesExt,
|
||||||
};
|
};
|
||||||
@ -29,7 +30,7 @@ use gitbutler_repo::{
|
|||||||
use gitbutler_stack::{
|
use gitbutler_stack::{
|
||||||
reconcile_claims, BranchOwnershipClaims, Stack, StackId, Target, VirtualBranchesHandle,
|
reconcile_claims, BranchOwnershipClaims, Stack, StackId, Target, VirtualBranchesHandle,
|
||||||
};
|
};
|
||||||
use gitbutler_stack_api::{commit_by_oid_or_change_id, StackExt};
|
use gitbutler_stack_api::StackExt;
|
||||||
use gitbutler_time::time::now_since_unix_epoch_ms;
|
use gitbutler_time::time::now_since_unix_epoch_ms;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -480,118 +481,11 @@ pub fn list_virtual_branches_cached(
|
|||||||
Ok((branches, status.skipped_files))
|
Ok((branches, status.skipped_files))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the stack series for the API.
|
|
||||||
/// Newest first, oldest last in the list
|
|
||||||
fn stack_series(
|
|
||||||
ctx: &CommandContext,
|
|
||||||
branch: &mut Stack,
|
|
||||||
default_target: &Target,
|
|
||||||
check_commit: &IsCommitIntegrated,
|
|
||||||
remote_commit_data: HashMap<CommitData, git2::Oid>,
|
|
||||||
) -> Result<(Vec<PatchSeries>, bool)> {
|
|
||||||
let mut requires_force = false;
|
|
||||||
let mut api_series: Vec<PatchSeries> = vec![];
|
|
||||||
let stack_series = branch.list_series(ctx)?;
|
|
||||||
let merge_base = ctx
|
|
||||||
.repository()
|
|
||||||
.merge_base(branch.head(), default_target.sha)?;
|
|
||||||
for series in stack_series.clone() {
|
|
||||||
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.clone().local_commits {
|
|
||||||
let commit =
|
|
||||||
commit_by_oid_or_change_id(&patch, ctx.repository(), branch.head(), merge_base)?;
|
|
||||||
let is_integrated = check_commit.is_integrated(&commit)?;
|
|
||||||
let copied_from_remote_id = CommitData::try_from(&commit)
|
|
||||||
.ok()
|
|
||||||
.and_then(|data| remote_commit_data.get(&data).copied());
|
|
||||||
let remote_commit_id = commit
|
|
||||||
.change_id()
|
|
||||||
.and_then(|change_id| {
|
|
||||||
series
|
|
||||||
.remote_commit_ids_by_change_id
|
|
||||||
.get(&change_id)
|
|
||||||
.cloned()
|
|
||||||
})
|
|
||||||
.or(copied_from_remote_id)
|
|
||||||
.or(if series.remote(&patch) {
|
|
||||||
Some(commit.id())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
});
|
|
||||||
if remote_commit_id.map_or(false, |id| commit.id() != id) {
|
|
||||||
requires_force = true;
|
|
||||||
}
|
|
||||||
let vcommit = commit_to_vbranch_commit(
|
|
||||||
ctx,
|
|
||||||
branch,
|
|
||||||
&commit,
|
|
||||||
is_integrated,
|
|
||||||
series.remote(&patch),
|
|
||||||
copied_from_remote_id,
|
|
||||||
remote_commit_id,
|
|
||||||
)?;
|
|
||||||
patches.push(vcommit);
|
|
||||||
}
|
|
||||||
patches.reverse();
|
|
||||||
let mut upstream_patches = vec![];
|
|
||||||
if let Some(upstream_reference) = upstream_reference.clone() {
|
|
||||||
let remote_head = ctx
|
|
||||||
.repository()
|
|
||||||
.find_reference(&upstream_reference)?
|
|
||||||
.peel_to_commit()?;
|
|
||||||
for patch in series.upstream_only_commits {
|
|
||||||
if let Ok(commit) = commit_by_oid_or_change_id(
|
|
||||||
&patch,
|
|
||||||
ctx.repository(),
|
|
||||||
remote_head.id(),
|
|
||||||
merge_base,
|
|
||||||
) {
|
|
||||||
let is_integrated = check_commit.is_integrated(&commit)?;
|
|
||||||
let vcommit = commit_to_vbranch_commit(
|
|
||||||
ctx,
|
|
||||||
branch,
|
|
||||||
&commit,
|
|
||||||
is_integrated,
|
|
||||||
true, // per definition
|
|
||||||
None, // per definition
|
|
||||||
Some(commit.id()),
|
|
||||||
)?;
|
|
||||||
upstream_patches.push(vcommit);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
upstream_patches.reverse();
|
|
||||||
api_series.push(PatchSeries {
|
|
||||||
name: series.head.name,
|
|
||||||
description: series.head.description,
|
|
||||||
upstream_reference,
|
|
||||||
patches,
|
|
||||||
upstream_patches,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
api_series.reverse();
|
|
||||||
|
|
||||||
// This is done for compatibility with the legacy flow.
|
|
||||||
// After a couple of weeks we can get rid of this.
|
|
||||||
if let Err(e) = branch.set_legacy_compatible_stack_reference(ctx) {
|
|
||||||
tracing::warn!("failed to set legacy compatible stack reference: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((api_series, requires_force))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The commit-data we can use for comparison to see which remote-commit was used to craete
|
/// The commit-data we can use for comparison to see which remote-commit was used to craete
|
||||||
/// a local commit from.
|
/// a local commit from.
|
||||||
/// Note that trees can't be used for comparison as these are typically rebased.
|
/// Note that trees can't be used for comparison as these are typically rebased.
|
||||||
#[derive(Hash, Eq, PartialEq)]
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
struct CommitData {
|
pub(crate) struct CommitData {
|
||||||
message: BString,
|
message: BString,
|
||||||
author: gix::actor::Signature,
|
author: gix::actor::Signature,
|
||||||
committer: gix::actor::Signature,
|
committer: gix::actor::Signature,
|
||||||
@ -1041,7 +935,7 @@ pub(crate) fn push(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IsCommitIntegrated<'repo> {
|
pub(crate) struct IsCommitIntegrated<'repo> {
|
||||||
repo: &'repo git2::Repository,
|
repo: &'repo git2::Repository,
|
||||||
target_commit_id: git2::Oid,
|
target_commit_id: git2::Oid,
|
||||||
remote_head_id: git2::Oid,
|
remote_head_id: git2::Oid,
|
||||||
@ -1073,7 +967,7 @@ impl<'repo> IsCommitIntegrated<'repo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IsCommitIntegrated<'_> {
|
impl IsCommitIntegrated<'_> {
|
||||||
fn is_integrated(&self, commit: &git2::Commit) -> Result<bool> {
|
pub(crate) fn is_integrated(&self, commit: &git2::Commit) -> Result<bool> {
|
||||||
if self.target_commit_id == commit.id() {
|
if self.target_commit_id == commit.id() {
|
||||||
// could not be integrated if heads are the same.
|
// could not be integrated if heads are the same.
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user