mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-24 13:37:34 +03:00
Implement integrate upstream commits for the stacked flow
refactor integrate upstream code for the stacked flow fix semantics
This commit is contained in:
parent
c5f20160bc
commit
b6ec442a4a
@ -169,7 +169,11 @@ pub fn push_base_branch(project: &Project, with_force: bool) -> Result<()> {
|
||||
base::push(&ctx, with_force)
|
||||
}
|
||||
|
||||
pub fn integrate_upstream_commits(project: &Project, branch_id: StackId) -> Result<()> {
|
||||
pub fn integrate_upstream_commits(
|
||||
project: &Project,
|
||||
branch_id: StackId,
|
||||
series_name: Option<String>,
|
||||
) -> Result<()> {
|
||||
let ctx = open_with_verify(project)?;
|
||||
assure_open_workspace_mode(&ctx)
|
||||
.context("Integrating upstream commits requires open workspace mode")?;
|
||||
@ -178,11 +182,20 @@ pub fn integrate_upstream_commits(project: &Project, branch_id: StackId) -> Resu
|
||||
SnapshotDetails::new(OperationKind::MergeUpstream),
|
||||
guard.write_permission(),
|
||||
);
|
||||
branch_upstream_integration::integrate_upstream_commits(
|
||||
&ctx,
|
||||
branch_id,
|
||||
guard.write_permission(),
|
||||
)
|
||||
if let Some(series_name) = series_name {
|
||||
branch_upstream_integration::integrate_upstream_commits_for_series(
|
||||
&ctx,
|
||||
branch_id,
|
||||
guard.write_permission(),
|
||||
series_name,
|
||||
)
|
||||
} else {
|
||||
branch_upstream_integration::integrate_upstream_commits(
|
||||
&ctx,
|
||||
branch_id,
|
||||
guard.write_permission(),
|
||||
)
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use gitbutler_command_context::CommandContext;
|
||||
use gitbutler_project::access::WorktreeWritePermission;
|
||||
use gitbutler_repo::{
|
||||
@ -6,7 +6,7 @@ use gitbutler_repo::{
|
||||
LogUntil, RepositoryExt as _,
|
||||
};
|
||||
use gitbutler_stack::StackId;
|
||||
use gitbutler_stack_api::StackExt;
|
||||
use gitbutler_stack_api::{commit_by_oid_or_change_id, StackExt};
|
||||
|
||||
use crate::{
|
||||
branch_trees::{
|
||||
@ -15,6 +15,107 @@ use crate::{
|
||||
conflicts, VirtualBranchesExt as _,
|
||||
};
|
||||
|
||||
pub fn integrate_upstream_commits_for_series(
|
||||
ctx: &CommandContext,
|
||||
branch_id: StackId,
|
||||
perm: &mut WorktreeWritePermission,
|
||||
series_name: String,
|
||||
) -> Result<()> {
|
||||
conflicts::is_conflicting(ctx, None)?;
|
||||
|
||||
let repository = ctx.repository();
|
||||
let vb_state = ctx.project().virtual_branches();
|
||||
|
||||
let branch = vb_state.get_branch_in_workspace(branch_id)?;
|
||||
let all_series = branch.list_series(ctx)?;
|
||||
|
||||
let default_target = vb_state.get_default_target()?;
|
||||
let remote = default_target.clone().push_remote_name.ok_or(anyhow!(
|
||||
"No remote has been configured for the target branch"
|
||||
))?;
|
||||
|
||||
let series = all_series
|
||||
.iter()
|
||||
.find(|series| series.head.name == series_name)
|
||||
.ok_or(anyhow!("Series not found"))?;
|
||||
let upstream_reference = series.head.remote_reference(remote.as_str())?;
|
||||
let remote_head = ctx
|
||||
.repository()
|
||||
.find_reference(&upstream_reference)?
|
||||
.peel_to_commit()?;
|
||||
let stack_merge_base = ctx
|
||||
.repository()
|
||||
.merge_base(branch.head(), default_target.sha)?;
|
||||
let upstream_to_local_merge_base = ctx
|
||||
.repository()
|
||||
.merge_base(branch.head(), remote_head.id())?;
|
||||
|
||||
// Rebasing will be performed if configured.
|
||||
// If the series to integrate is not the latest one, it will also do a rebase.
|
||||
let new_stack_head = if branch.allow_rebasing || Some(series) != all_series.first() {
|
||||
// commits to rebase
|
||||
let mut ordered_commits_to_rebase = vec![];
|
||||
for series in all_series.clone() {
|
||||
// if this is the series that is getting the upstream commits, they are added first
|
||||
if series.head.name == series_name {
|
||||
for id in series.upstream_only_commits.iter() {
|
||||
let commit = commit_by_oid_or_change_id(
|
||||
id,
|
||||
ctx,
|
||||
remote_head.id(),
|
||||
upstream_to_local_merge_base,
|
||||
)?;
|
||||
ordered_commits_to_rebase.push(commit.id());
|
||||
}
|
||||
}
|
||||
// now add the existing local commits to the rebase list
|
||||
for id in series.local_commits.iter() {
|
||||
let commit = commit_by_oid_or_change_id(id, ctx, branch.head(), stack_merge_base)?;
|
||||
ordered_commits_to_rebase.push(commit.id());
|
||||
}
|
||||
}
|
||||
cherry_rebase_group(
|
||||
repository,
|
||||
upstream_to_local_merge_base,
|
||||
&ordered_commits_to_rebase,
|
||||
)?
|
||||
} else {
|
||||
// If rebase is not allowed AND this is the latest series - create a merge commit on top
|
||||
let series_head_commit = commit_by_oid_or_change_id(
|
||||
&series.head.target,
|
||||
ctx,
|
||||
branch.head(),
|
||||
upstream_to_local_merge_base,
|
||||
)?;
|
||||
let merge_commit = gitbutler_merge_commits(
|
||||
repository,
|
||||
series_head_commit,
|
||||
remote_head.clone(),
|
||||
&series.head.name, // for error messages only
|
||||
&series
|
||||
.head
|
||||
.remote_reference(&default_target.sha.to_string())?, // for error messages only
|
||||
)?;
|
||||
// the new merge commit is now the stack and series head
|
||||
merge_commit.id()
|
||||
};
|
||||
|
||||
// Find what the new head and branch tree should be
|
||||
let BranchHeadAndTree { head, tree } = compute_updated_branch_head_for_commits(
|
||||
repository,
|
||||
branch.head(),
|
||||
branch.tree,
|
||||
new_stack_head,
|
||||
)?;
|
||||
|
||||
let mut branch = branch.clone();
|
||||
|
||||
branch.set_stack_head(ctx, head, Some(tree))?;
|
||||
checkout_branch_trees(ctx, perm)?;
|
||||
crate::integration::update_workspace_commit(&vb_state, ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Integrates upstream work from a remote branch.
|
||||
///
|
||||
/// Any to-be integrated commits that are upstream will be placed at the bottom
|
||||
|
@ -116,9 +116,10 @@ pub mod commands {
|
||||
projects: State<'_, projects::Controller>,
|
||||
project_id: ProjectId,
|
||||
branch: StackId,
|
||||
series_name: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let project = projects.get(project_id)?;
|
||||
gitbutler_branch_actions::integrate_upstream_commits(&project, branch)?;
|
||||
gitbutler_branch_actions::integrate_upstream_commits(&project, branch, series_name)?;
|
||||
emit_vbranches(&windows, project_id);
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user