Skip to content

Commit

Permalink
refactor rebase -r to use new_parents from rewrite.rs
Browse files Browse the repository at this point in the history
This makes the code more robust, allowing complex rebases of perhaps already
rebased commits. Thus, it is no longer dependent on jj-vcs#2600. We could probably
achieve that in other ways, but it would become quite difficult to reason about
when implementing `rebase -r --before/--after`.
  • Loading branch information
ilyagr committed Nov 28, 2023
1 parent af04e3d commit 553a770
Showing 1 changed file with 26 additions and 28 deletions.
54 changes: 26 additions & 28 deletions cli/src/commands/rebase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ use jj_lib::backend::{CommitId, ObjectId};
use jj_lib::commit::Commit;
use jj_lib::repo::{ReadonlyRepo, Repo};
use jj_lib::revset::{RevsetExpression, RevsetIteratorExt};
use jj_lib::rewrite::{rebase_commit, rebase_commit_with_options, EmptyBehaviour, RebaseOptions};
use jj_lib::rewrite::{
new_parents_via_mapping, rebase_commit, rebase_commit_with_options, EmptyBehaviour,
RebaseOptions,
};
use jj_lib::settings::UserSettings;
use tracing::instrument;

Expand Down Expand Up @@ -370,45 +373,40 @@ fn rebase_revision(

rebased_commit_ids.insert(
child_commit.id().clone(),
rebase_commit(settings, tx.mut_repo(), child_commit, &new_child_parents)?
.id()
.clone(),
vec![
rebase_commit(settings, tx.mut_repo(), child_commit, &new_child_parents)?
.id()
.clone(),
],
);
}
// Now, rebase the descendants of the children.
// TODO(ilyagr): Consider making it possible for these descendants to become
// emptied, like --skip_empty. This would require writing careful tests.
rebased_commit_ids.extend(tx.mut_repo().rebase_descendants_return_map(settings)?);
rebased_commit_ids.extend(
tx.mut_repo()
.rebase_descendants_return_map(settings)?
.into_iter()
.map(|(from, to)| (from, vec![to])),
);
let num_rebased_descendants = rebased_commit_ids.len();

// We now update `new_parents` to account for the rebase of all of
// `old_commit`'s descendants. Even if some of the original `new_parents`
// were descendants of `old_commit`, this will no longer be the case after
// the update.
//
// To make the update simpler, we assume that each commit was rewritten only
// once; we don't have a situation where both `(A,B)` and `(B,C)` are in
// `rebased_commit_ids`.
//
// TODO(ilyagr): This assumption relies on the fact that, after
// `rebase_descendants`, a child of `old_commit` cannot also be a direct
// child of `old_commit`. This fact will likely change, see
// https://github.com/martinvonz/jj/issues/2600. So, the code needs to be
// updated before that happens. This would also affect
// `test_rebase_with_child_and_descendant_bug_2600`.
//
// The issue is that if a child and a descendant were the same commit, it
// would be rebased both by `rebase_commit` above and by
// `rebase_descendants_return_map`.
let new_parents: Vec<_> = new_parents
let new_parent_ids = new_parents_via_mapping(
new_parents
.iter()
.map(|commit| commit.id())
.cloned()
.collect_vec()
.as_slice(),
&rebased_commit_ids,
);
let new_parents: Vec<_> = new_parent_ids
.iter()
.map(|new_parent| {
rebased_commit_ids
.get(new_parent.id())
.map_or(Ok(new_parent.clone()), |rebased_new_parent_id| {
tx.repo().store().get_commit(rebased_new_parent_id)
})
})
.map(|id| tx.mut_repo().store().get_commit(id))
.try_collect()?;

// Finally, it's safe to rebase `old_commit`. At this point, it should no longer
Expand Down

0 comments on commit 553a770

Please sign in to comment.