|
1 | 1 | use crate::{hunk::VirtualBranchHunk, status::get_applied_status_cached, VirtualBranchesExt}; |
2 | 2 | use anyhow::{anyhow, bail, Context, Result}; |
3 | | -use bstr::{BString, ByteSlice}; |
| 3 | +use bstr::BString; |
4 | 4 | use but_rebase::RebaseStep; |
5 | 5 | use but_workspace::stack_ext::StackExt; |
6 | 6 | use gitbutler_branch::dedup; |
7 | 7 | use gitbutler_branch::BranchUpdateRequest; |
8 | 8 | use gitbutler_cherry_pick::RepositoryExt as _; |
9 | 9 | use gitbutler_command_context::CommandContext; |
10 | | -use gitbutler_commit::{commit_ext::CommitExt, commit_headers::HasCommitHeaders}; |
| 10 | +use gitbutler_commit::commit_ext::CommitExt; |
11 | 11 | use gitbutler_diff::GitHunk; |
12 | 12 | use gitbutler_oxidize::{ |
13 | 13 | git2_signature_to_gix_signature, git2_to_gix_object_id, gix_to_git2_oid, GixRepositoryExt, |
@@ -486,204 +486,6 @@ pub fn is_remote_branch_mergeable( |
486 | 486 | Ok(mergeable) |
487 | 487 | } |
488 | 488 |
|
489 | | -// this function takes a list of file ownership from a "from" commit and "moves" |
490 | | -// those changes to a "to" commit in a branch. This allows users to drag changes |
491 | | -// from one commit to another. |
492 | | -// if the "to" commit is below the "from" commit, the changes are simply added to the "to" commit |
493 | | -// and the rebase should be simple. if the "to" commit is above the "from" commit, |
494 | | -// the changes need to be removed from the "from" commit, everything rebased, |
495 | | -// then added to the "to" commit and everything above that rebased again. |
496 | | -// |
497 | | -// NB: It appears that this function is semi-broken when the "to" commit is above the "from" commit. |
498 | | -// Ths changes are indeed removed from the "from" commit, but they end up in the workspace and not the "to" commit. |
499 | | -// This was broken before the migration to the rebase engine. |
500 | | -// The way the trees of "diffs to keep" and "diffs to amend" are computed with gitbutler_diff::write::hunks_onto_commit is incredibly sketchy |
501 | | -pub(crate) fn move_commit_file( |
502 | | - ctx: &CommandContext, |
503 | | - stack_id: StackId, |
504 | | - from_commit_id: git2::Oid, |
505 | | - to_commit_id: git2::Oid, |
506 | | - target_ownership: &BranchOwnershipClaims, |
507 | | -) -> Result<git2::Oid> { |
508 | | - let vb_state = ctx.project().virtual_branches(); |
509 | | - |
510 | | - let default_target = vb_state.get_default_target()?; |
511 | | - |
512 | | - let mut stack = vb_state.get_stack_in_workspace(stack_id)?; |
513 | | - let gix_repo = ctx.gix_repo()?; |
514 | | - let merge_base = stack.merge_base(ctx)?; |
515 | | - |
516 | | - // first, let's get the from commit data and it's parent data |
517 | | - let from_commit = ctx |
518 | | - .repo() |
519 | | - .find_commit(from_commit_id) |
520 | | - .context("failed to find commit")?; |
521 | | - let from_tree = from_commit.tree().context("failed to find tree")?; |
522 | | - let from_parent = from_commit.parent(0).context("failed to find parent")?; |
523 | | - let from_parent_tree = from_parent.tree().context("failed to find parent tree")?; |
524 | | - |
525 | | - // ok, what is the entire patch introduced in the "from" commit? |
526 | | - // we need to remove the parts of this patch that are in target_ownership (the parts we're moving) |
527 | | - // and then apply the rest to the parent tree of the "from" commit to |
528 | | - // create the new "from" commit without the changes we're moving |
529 | | - let from_commit_diffs = gitbutler_diff::trees(ctx.repo(), &from_parent_tree, &from_tree, true) |
530 | | - .context("failed to diff trees")?; |
531 | | - |
532 | | - // filter from_commit_diffs to HashMap<filepath, Vec<GitHunk>> only for hunks NOT in target_ownership |
533 | | - // this is the patch parts we're keeping |
534 | | - let diffs_to_keep = from_commit_diffs |
535 | | - .iter() |
536 | | - .filter_map(|(filepath, file_diff)| { |
537 | | - let hunks = file_diff |
538 | | - .hunks |
539 | | - .iter() |
540 | | - .filter(|hunk| { |
541 | | - !target_ownership.claims.iter().any(|file_ownership| { |
542 | | - file_ownership.file_path.eq(filepath) |
543 | | - && file_ownership.hunks.iter().any(|owned_hunk| { |
544 | | - owned_hunk.start == hunk.new_start |
545 | | - && owned_hunk.end == hunk.new_start + hunk.new_lines |
546 | | - }) |
547 | | - }) |
548 | | - }) |
549 | | - .cloned() |
550 | | - .collect::<Vec<_>>(); |
551 | | - if hunks.is_empty() { |
552 | | - None |
553 | | - } else { |
554 | | - Some((filepath.clone(), hunks)) |
555 | | - } |
556 | | - }) |
557 | | - .collect::<HashMap<_, _>>(); |
558 | | - |
559 | | - // write our new tree and commit for the new "from" commit without the moved changes |
560 | | - let new_from_tree_id = |
561 | | - gitbutler_diff::write::hunks_onto_commit(ctx, from_parent.id(), &diffs_to_keep)?; |
562 | | - let new_from_tree = &ctx |
563 | | - .repo() |
564 | | - .find_tree(new_from_tree_id) |
565 | | - .with_context(|| "tree {new_from_tree_oid} not found")?; |
566 | | - let new_from_commit_oid = ctx |
567 | | - .repo() |
568 | | - .commit_with_signature( |
569 | | - None, |
570 | | - &from_commit.author(), |
571 | | - &from_commit.committer(), |
572 | | - &from_commit.message_bstr().to_str_lossy(), |
573 | | - new_from_tree, |
574 | | - &[&from_parent], |
575 | | - from_commit.gitbutler_headers(), |
576 | | - ) |
577 | | - .context("commit failed")?; |
578 | | - |
579 | | - // rebase swapping the from_commit_oid with the new_from_commit_oid |
580 | | - let mut steps = stack.as_rebase_steps(ctx, &gix_repo)?; |
581 | | - // replace the "from" commit in the rebase steps with the new "from" commit which has the moved changes removed |
582 | | - for step in steps.iter_mut() { |
583 | | - if let RebaseStep::Pick { commit_id, .. } = step { |
584 | | - if *commit_id == from_commit_id.to_gix() { |
585 | | - *commit_id = new_from_commit_oid.to_gix(); |
586 | | - } |
587 | | - } |
588 | | - } |
589 | | - let mut rebase = but_rebase::Rebase::new(&gix_repo, merge_base, None)?; |
590 | | - rebase.steps(steps)?; |
591 | | - rebase.rebase_noops(false); |
592 | | - let outcome = rebase.rebase()?; |
593 | | - // ensure that the stack here has been updated. |
594 | | - stack.set_heads_from_rebase_output(ctx, outcome.references)?; |
595 | | - |
596 | | - // Discover the new id of the commit to amend `to_commit_id` from the output of the first rebas |
597 | | - let to_commit_id = outcome |
598 | | - .commit_mapping |
599 | | - .iter() |
600 | | - .find(|(_base, old, _new)| old == &to_commit_id.to_gix()) |
601 | | - .map(|(_base, _old, new)| new.to_git2()) |
602 | | - .ok_or_else(|| anyhow!("failed to find the to_ammend_commit after the initial rebase"))?; |
603 | | - |
604 | | - let to_commit = ctx |
605 | | - .repo() |
606 | | - .find_commit(to_commit_id) |
607 | | - .context("failed to find commit")?; |
608 | | - let to_commit_parents: Vec<_> = to_commit.parents().collect(); |
609 | | - |
610 | | - // get a list of all the diffs across all the virtual branches |
611 | | - let base_file_diffs = gitbutler_diff::workdir(ctx.repo(), default_target.sha) |
612 | | - .context("failed to diff workdir")?; |
613 | | - |
614 | | - // filter base_file_diffs to HashMap<filepath, Vec<GitHunk>> only for hunks in target_ownership |
615 | | - // this is essentially the group of patches that we're "moving" |
616 | | - let diffs_to_amend = target_ownership |
617 | | - .claims |
618 | | - .iter() |
619 | | - .filter_map(|file_ownership| { |
620 | | - let hunks = base_file_diffs |
621 | | - .get(&file_ownership.file_path) |
622 | | - .map(|hunks| { |
623 | | - hunks |
624 | | - .hunks |
625 | | - .iter() |
626 | | - .filter(|hunk| { |
627 | | - file_ownership.hunks.iter().any(|owned_hunk| { |
628 | | - owned_hunk.start == hunk.new_start |
629 | | - && owned_hunk.end == hunk.new_start + hunk.new_lines |
630 | | - }) |
631 | | - }) |
632 | | - .cloned() |
633 | | - .collect::<Vec<_>>() |
634 | | - }) |
635 | | - .unwrap_or_default(); |
636 | | - if hunks.is_empty() { |
637 | | - None |
638 | | - } else { |
639 | | - Some((file_ownership.file_path.clone(), hunks)) |
640 | | - } |
641 | | - }) |
642 | | - .collect::<HashMap<_, _>>(); |
643 | | - // apply diffs_to_amend to the commit tree |
644 | | - // and write a new commit with the changes we're moving |
645 | | - // let new_tree_oid = |
646 | | - // gitbutler_diff::write::hunks_onto_commit(ctx, to_commit_id, &diffs_to_amend)?; |
647 | | - let new_tree_oid = |
648 | | - gitbutler_diff::write::hunks_onto_tree(ctx, &to_commit.tree()?, &diffs_to_amend, true)?; |
649 | | - |
650 | | - let new_tree = ctx |
651 | | - .repo() |
652 | | - .find_tree(new_tree_oid) |
653 | | - .context("failed to find new tree")?; |
654 | | - let new_to_commit_oid = ctx |
655 | | - .repo() |
656 | | - .commit_with_signature( |
657 | | - None, |
658 | | - &to_commit.author(), |
659 | | - &to_commit.committer(), |
660 | | - &to_commit.message_bstr().to_str_lossy(), |
661 | | - &new_tree, |
662 | | - &to_commit_parents.iter().collect::<Vec<_>>(), |
663 | | - to_commit.gitbutler_headers(), |
664 | | - ) |
665 | | - .context("failed to create commit")?; |
666 | | - |
667 | | - // another rebase |
668 | | - let mut steps = stack.as_rebase_steps(ctx, &gix_repo)?; |
669 | | - // replace the "to" commit in the rebase steps with the new "to" commit which has the moved changes added |
670 | | - for step in steps.iter_mut() { |
671 | | - if let RebaseStep::Pick { commit_id, .. } = step { |
672 | | - if *commit_id == to_commit_id.to_gix() { |
673 | | - *commit_id = new_to_commit_oid.to_gix(); |
674 | | - } |
675 | | - } |
676 | | - } |
677 | | - let mut rebase = but_rebase::Rebase::new(&gix_repo, merge_base, None)?; |
678 | | - rebase.steps(steps)?; |
679 | | - rebase.rebase_noops(false); |
680 | | - let outcome = rebase.rebase()?; |
681 | | - stack.set_heads_from_rebase_output(ctx, outcome.references)?; |
682 | | - stack.set_stack_head(&vb_state, &gix_repo, outcome.top_commit.to_git2(), None)?; |
683 | | - // todo: maybe update the workspace commit here? |
684 | | - Ok(new_to_commit_oid) |
685 | | -} |
686 | | - |
687 | 489 | // create and insert a blank commit (no tree change) either above or below a commit |
688 | 490 | // if offset is positive, insert below, if negative, insert above |
689 | 491 | // return map of the updated commit ids |
|
0 commit comments