|
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