mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 14:00:51 +00:00
rewrite: add a function for rebasing descendant commits
This should be useful in lots of places. For example, `jj rebase -r` currently rebases all descendants, because that's what the auto-evolve feature does. I think it would be nice to instead copy from Mercurial's `-s` flag for also rebasing descendants. Then `jj rebase -r` can be made to pull a commit out of a stack, rebasing descendants onto the rebased commit's parents. I also intend to use this functionality for rebasing descendants when remote branches have been rewritten.
This commit is contained in:
parent
658b41b4e9
commit
4e0a89b3dd
3 changed files with 478 additions and 2 deletions
|
@ -173,6 +173,7 @@ pub enum RevsetParseError {
|
|||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum RevsetExpression {
|
||||
None,
|
||||
Commits(Vec<CommitId>),
|
||||
Symbol(String),
|
||||
Parents(Rc<RevsetExpression>),
|
||||
Children {
|
||||
|
@ -218,6 +219,14 @@ impl RevsetExpression {
|
|||
Rc::new(RevsetExpression::Symbol(value))
|
||||
}
|
||||
|
||||
pub fn commit(commit_id: CommitId) -> Rc<RevsetExpression> {
|
||||
RevsetExpression::commits(vec![commit_id])
|
||||
}
|
||||
|
||||
pub fn commits(commit_ids: Vec<CommitId>) -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::Commits(commit_ids))
|
||||
}
|
||||
|
||||
pub fn all_heads() -> Rc<RevsetExpression> {
|
||||
Rc::new(RevsetExpression::AllHeads)
|
||||
}
|
||||
|
@ -962,8 +971,7 @@ pub fn evaluate_expression<'repo>(
|
|||
RevsetExpression::None => Ok(Box::new(EagerRevset {
|
||||
index_entries: vec![],
|
||||
})),
|
||||
RevsetExpression::Symbol(symbol) => {
|
||||
let commit_ids = resolve_symbol(repo, symbol)?;
|
||||
RevsetExpression::Commits(commit_ids) => {
|
||||
let index = repo.index();
|
||||
let mut index_entries = commit_ids
|
||||
.iter()
|
||||
|
@ -972,6 +980,10 @@ pub fn evaluate_expression<'repo>(
|
|||
index_entries.sort_by_key(|b| Reverse(b.position()));
|
||||
Ok(Box::new(EagerRevset { index_entries }))
|
||||
}
|
||||
RevsetExpression::Symbol(symbol) => {
|
||||
let commit_ids = resolve_symbol(repo, symbol)?;
|
||||
evaluate_expression(repo, &RevsetExpression::Commits(commit_ids))
|
||||
}
|
||||
RevsetExpression::Parents(base_expression) => {
|
||||
// TODO: Make this lazy
|
||||
let base_set = base_expression.evaluate(repo)?;
|
||||
|
|
|
@ -12,13 +12,17 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::commit::Commit;
|
||||
use crate::commit_builder::CommitBuilder;
|
||||
use crate::repo::{MutableRepo, RepoRef};
|
||||
use crate::repo_path::RepoPath;
|
||||
use crate::revset::RevsetExpression;
|
||||
use crate::settings::UserSettings;
|
||||
use crate::store::CommitId;
|
||||
use crate::tree::{merge_trees, Tree};
|
||||
|
||||
pub fn merge_commit_trees(repo: RepoRef, commits: &[Commit]) -> Tree {
|
||||
|
@ -93,3 +97,113 @@ pub fn back_out_commit(
|
|||
))
|
||||
.write_to_repo(mut_repo)
|
||||
}
|
||||
|
||||
/// Rebases descendants of a commit onto a new commit (or several).
|
||||
// TODO: Should there be an option to drop empty commits (and/or an option to
|
||||
// drop empty commits only if they weren't already empty)? Or maybe that
|
||||
// shouldn't be this type's job.
|
||||
pub struct DescendantRebaser<'settings, 'repo> {
|
||||
settings: &'settings UserSettings,
|
||||
mut_repo: &'repo mut MutableRepo,
|
||||
old_parent_id: CommitId,
|
||||
new_parent_ids: Vec<CommitId>,
|
||||
// In reverse order, so we can remove the last one to rebase first.
|
||||
to_rebase: Vec<CommitId>,
|
||||
rebased: HashMap<CommitId, CommitId>,
|
||||
}
|
||||
|
||||
impl<'settings, 'repo> DescendantRebaser<'settings, 'repo> {
|
||||
pub fn new(
|
||||
settings: &'settings UserSettings,
|
||||
mut_repo: &'repo mut MutableRepo,
|
||||
old_parent_id: CommitId,
|
||||
new_parent_ids: Vec<CommitId>,
|
||||
) -> DescendantRebaser<'settings, 'repo> {
|
||||
let expression = RevsetExpression::commit(old_parent_id.clone())
|
||||
.descendants(&RevsetExpression::all_non_obsolete_heads())
|
||||
.minus(
|
||||
&RevsetExpression::commit(old_parent_id.clone())
|
||||
.union(&RevsetExpression::commits(new_parent_ids.clone()))
|
||||
.ancestors(),
|
||||
);
|
||||
let revset = expression.evaluate(mut_repo.as_repo_ref()).unwrap();
|
||||
let mut to_rebase = vec![];
|
||||
for index_entry in revset.iter() {
|
||||
to_rebase.push(index_entry.commit_id());
|
||||
}
|
||||
drop(revset);
|
||||
|
||||
DescendantRebaser {
|
||||
settings,
|
||||
mut_repo,
|
||||
old_parent_id,
|
||||
new_parent_ids,
|
||||
to_rebase,
|
||||
rebased: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a map from `CommitId` of old commit to new commit. Includes the
|
||||
/// commits rebase so far. Does not include the inputs passed to
|
||||
/// `rebase_descendants`.
|
||||
pub fn rebased(&self) -> &HashMap<CommitId, CommitId> {
|
||||
&self.rebased
|
||||
}
|
||||
|
||||
pub fn rebase_next(&mut self) -> Option<RebasedDescendant> {
|
||||
self.to_rebase.pop().map(|old_commit_id| {
|
||||
let old_commit = self.mut_repo.store().get_commit(&old_commit_id).unwrap();
|
||||
let mut new_parent_ids = vec![];
|
||||
let old_parent_ids = old_commit.parent_ids();
|
||||
for old_parent_id in &old_parent_ids {
|
||||
if old_parent_id == &self.old_parent_id {
|
||||
new_parent_ids.extend(self.new_parent_ids.clone());
|
||||
} else if let Some(new_parent_id) = self.rebased.get(old_parent_id) {
|
||||
new_parent_ids.push(new_parent_id.clone());
|
||||
} else {
|
||||
new_parent_ids.push(old_parent_id.clone());
|
||||
};
|
||||
}
|
||||
if new_parent_ids == old_parent_ids {
|
||||
RebasedDescendant::AlreadyInPlace
|
||||
} else {
|
||||
// Don't create commit where one parent is an ancestor of another.
|
||||
let head_set: HashSet<_> = self
|
||||
.mut_repo
|
||||
.index()
|
||||
.heads(&new_parent_ids)
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
let new_parent_ids = new_parent_ids
|
||||
.into_iter()
|
||||
.filter(|new_parent| head_set.contains(new_parent))
|
||||
.collect();
|
||||
let new_commit = CommitBuilder::for_rewrite_from(
|
||||
self.settings,
|
||||
self.mut_repo.store(),
|
||||
&old_commit,
|
||||
)
|
||||
.set_parents(new_parent_ids)
|
||||
.write_to_repo(self.mut_repo);
|
||||
self.rebased.insert(old_commit_id, new_commit.id().clone());
|
||||
RebasedDescendant::Rebased {
|
||||
old_commit,
|
||||
new_commit,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rebase_all(&mut self) {
|
||||
while self.rebase_next().is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RebasedDescendant {
|
||||
AlreadyInPlace,
|
||||
Rebased {
|
||||
old_commit: Commit,
|
||||
new_commit: Commit,
|
||||
},
|
||||
}
|
||||
|
|
350
lib/tests/test_rewrite.rs
Normal file
350
lib/tests/test_rewrite.rs
Normal file
|
@ -0,0 +1,350 @@
|
|||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use jujutsu_lib::rewrite::DescendantRebaser;
|
||||
use jujutsu_lib::testutils;
|
||||
use jujutsu_lib::testutils::CommitGraphBuilder;
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_sideways(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 2 was replaced by commit 6. Commits 3-5 should be rebased.
|
||||
//
|
||||
// 6
|
||||
// | 4
|
||||
// | 3 5
|
||||
// | |/
|
||||
// | 2
|
||||
// |/
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit3]);
|
||||
let commit5 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit6 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit2.id().clone(),
|
||||
vec![commit6.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 3);
|
||||
let new_commit3 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit3.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit3.change_id(), commit3.change_id());
|
||||
assert_eq!(new_commit3.parent_ids(), vec![commit6.id().clone()]);
|
||||
let new_commit4 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit4.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit4.change_id(), commit4.change_id());
|
||||
assert_eq!(new_commit4.parent_ids(), vec![new_commit3.id().clone()]);
|
||||
let new_commit5 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit5.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit5.change_id(), commit5.change_id());
|
||||
assert_eq!(new_commit5.parent_ids(), vec![commit6.id().clone()]);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_forward(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 2 was replaced by commit 3. Commit 5 should be rebased (commit 4 is
|
||||
// already in place).
|
||||
//
|
||||
// 4
|
||||
// 3 5
|
||||
// |/
|
||||
// 2
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let _commit4 = graph_builder.commit_with_parents(&[&commit3]);
|
||||
let commit5 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit2.id().clone(),
|
||||
vec![commit3.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 1);
|
||||
let new_commit5 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit5.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit5.change_id(), commit5.change_id());
|
||||
assert_eq!(new_commit5.parent_ids(), vec![commit3.id().clone()]);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_backward(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 3 was replaced by commit 2. Commit 4 should be rebased.
|
||||
//
|
||||
// 4
|
||||
// 3
|
||||
// 2
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit3]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit3.id().clone(),
|
||||
vec![commit2.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 1);
|
||||
let new_commit4 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit4.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit4.change_id(), commit4.change_id());
|
||||
assert_eq!(new_commit4.parent_ids(), vec![commit2.id().clone()]);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_internal_merge(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 2 was replaced by commit 6. Commits 3-5 should be rebased.
|
||||
//
|
||||
// 6
|
||||
// | 5
|
||||
// | |\
|
||||
// | 3 4
|
||||
// | |/
|
||||
// | 2
|
||||
// |/
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit5 = graph_builder.commit_with_parents(&[&commit3, &commit4]);
|
||||
let commit6 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit2.id().clone(),
|
||||
vec![commit6.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 3);
|
||||
let new_commit3 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit3.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit3.change_id(), commit3.change_id());
|
||||
assert_eq!(new_commit3.parent_ids(), vec![commit6.id().clone()]);
|
||||
let new_commit4 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit4.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit4.change_id(), commit4.change_id());
|
||||
assert_eq!(new_commit4.parent_ids(), vec![commit6.id().clone()]);
|
||||
let new_commit5 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit5.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit5.change_id(), commit5.change_id());
|
||||
assert_eq!(
|
||||
new_commit5.parent_ids(),
|
||||
vec![new_commit3.id().clone(), new_commit4.id().clone()]
|
||||
);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_external_merge(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 3 was replaced by commit 6. Commits 5 should be rebased. The rebased
|
||||
// commit 5 should have 6 as first parent and commit 4 as second parent.
|
||||
//
|
||||
// 6
|
||||
// | 5
|
||||
// | |\
|
||||
// | 3 4
|
||||
// | |/
|
||||
// | 2
|
||||
// |/
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit2]);
|
||||
let commit5 = graph_builder.commit_with_parents(&[&commit3, &commit4]);
|
||||
let commit6 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit3.id().clone(),
|
||||
vec![commit6.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 1);
|
||||
let new_commit5 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit5.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit5.change_id(), commit5.change_id());
|
||||
assert_eq!(
|
||||
new_commit5.parent_ids(),
|
||||
vec![commit6.id().clone(), commit4.id().clone()]
|
||||
);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_degenerate_merge(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 2 was replaced by commit 1 (maybe it was pruned). Commit 4 should get
|
||||
// rebased to have only 3 as parent (not 1 and 3).
|
||||
//
|
||||
// 4
|
||||
// |\
|
||||
// 2 3
|
||||
// |/
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit2, &commit3]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit2.id().clone(),
|
||||
vec![commit1.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 1);
|
||||
let new_commit4 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit4.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit4.change_id(), commit4.change_id());
|
||||
assert_eq!(new_commit4.parent_ids(), vec![commit3.id().clone()]);
|
||||
|
||||
tx.discard();
|
||||
}
|
||||
|
||||
#[test_case(false ; "local store")]
|
||||
#[test_case(true ; "git store")]
|
||||
fn test_rebase_descendants_widen_merge(use_git: bool) {
|
||||
let settings = testutils::user_settings();
|
||||
let (_temp_dir, repo) = testutils::init_repo(&settings, use_git);
|
||||
|
||||
// Commit 5 was replaced by commits 2 and 3 (maybe 5 was pruned). Commit 6
|
||||
// should get rebased to have 2, 3, and 4 as parents (in that order).
|
||||
//
|
||||
// 6
|
||||
// |\
|
||||
// 5 \
|
||||
// |\ \
|
||||
// 2 3 4
|
||||
// \|/
|
||||
// 1
|
||||
let mut tx = repo.start_transaction("test");
|
||||
let mut graph_builder = CommitGraphBuilder::new(&settings, tx.mut_repo());
|
||||
let commit1 = graph_builder.initial_commit();
|
||||
let commit2 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit3 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit4 = graph_builder.commit_with_parents(&[&commit1]);
|
||||
let commit5 = graph_builder.commit_with_parents(&[&commit2, &commit3]);
|
||||
let commit6 = graph_builder.commit_with_parents(&[&commit5, &commit4]);
|
||||
|
||||
let mut rebaser = DescendantRebaser::new(
|
||||
&settings,
|
||||
tx.mut_repo(),
|
||||
commit5.id().clone(),
|
||||
vec![commit2.id().clone(), commit3.id().clone()],
|
||||
);
|
||||
rebaser.rebase_all();
|
||||
let rebased = rebaser.rebased();
|
||||
assert_eq!(rebased.len(), 1);
|
||||
let new_commit6 = repo
|
||||
.store()
|
||||
.get_commit(rebased.get(commit6.id()).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(new_commit6.change_id(), commit6.change_id());
|
||||
assert_eq!(
|
||||
new_commit6.parent_ids(),
|
||||
vec![
|
||||
commit2.id().clone(),
|
||||
commit3.id().clone(),
|
||||
commit4.id().clone()
|
||||
]
|
||||
);
|
||||
|
||||
tx.discard();
|
||||
}
|
Loading…
Reference in a new issue