def test_plan_with_already_merged_skip_merges(self): """We need to use a merge base that makes sense. A | \ B D | \| C E Rebasing E on C should result in: A -> B -> C -> D' with a plan of: D -> (D', [C]) """ parents_map = { "A": (), "B": ("A",), "C": ("B",), "D": ("A",), "E": ("D", "B") } graph = Graph(DictParentsProvider(parents_map)) self.assertEquals({"D": ("D'", ("C",))}, generate_simple_plan(["D", "E"], "D", None, "C", graph, lambda y, _: y+"'", True))
def test_simple_plan_creator(self): wt = self.make_branch_and_tree('.') b = wt.branch file('hello', 'w').write('hello world') wt.add('hello') wt.commit(message='add hello', rev_id="bla") file('hello', 'w').write('world') wt.commit(message='change hello', rev_id="bloe") wt.set_last_revision("bla") b.generate_revision_history("bla") file('hello', 'w').write('world') wt.commit(message='change hello', rev_id="bla2") b.repository.lock_read() graph = b.repository.get_graph() self.assertEquals({'bla2': ('newbla2', ("bloe",))}, generate_simple_plan( graph.find_difference(b.last_revision(),"bla")[0], "bla2", None, "bloe", graph, lambda y, _: "new"+y)) b.repository.unlock()
def run( self, upstream_location=None, onto=None, revision=None, merge_type=None, verbose=False, dry_run=False, always_rebase_merges=False, pending_merges=False, directory=".", ): from bzrlib.branch import Branch from bzrlib.revisionspec import RevisionSpec from bzrlib.workingtree import WorkingTree from bzrlib.plugins.rewrite.rebase import ( generate_simple_plan, rebase, RebaseState1, WorkingTreeRevisionRewriter, regenerate_default_revid, rebase_todo, ) if revision is not None and pending_merges: raise BzrCommandError(gettext("--revision and --pending-merges are mutually exclusive")) wt = WorkingTree.open_containing(directory)[0] wt.lock_write() try: state = RebaseState1(wt) if upstream_location is None: if pending_merges: upstream_location = directory else: upstream_location = wt.branch.get_parent() if upstream_location is None: raise BzrCommandError(gettext("No upstream branch specified.")) note(gettext("Rebasing on %s"), upstream_location) upstream = Branch.open_containing(upstream_location)[0] upstream_repository = upstream.repository upstream_revision = upstream.last_revision() # Abort if there already is a plan file if state.has_plan(): raise BzrCommandError( gettext( "A rebase operation was interrupted. " "Continue using 'bzr rebase-continue' or abort using 'bzr " "rebase-abort'" ) ) start_revid = None stop_revid = None if revision is not None: if len(revision) == 1: if revision[0] is not None: stop_revid = revision[0].as_revision_id(wt.branch) elif len(revision) == 2: if revision[0] is not None: start_revid = revision[0].as_revision_id(wt.branch) if revision[1] is not None: stop_revid = revision[1].as_revision_id(wt.branch) else: raise BzrCommandError(gettext("--revision takes only one or two arguments")) if pending_merges: wt_parents = wt.get_parent_ids() if len(wt_parents) in (0, 1): raise BzrCommandError(gettext("No pending merges present.")) elif len(wt_parents) > 2: raise BzrCommandError(gettext("Rebasing more than one pending merge not supported")) stop_revid = wt_parents[1] assert stop_revid is not None, "stop revid invalid" # Check for changes in the working tree. if not pending_merges and wt.basis_tree().changes_from(wt).has_changed(): raise UncommittedChanges(wt) # Pull required revisions wt.branch.repository.fetch(upstream_repository, upstream_revision) if onto is None: onto = upstream.last_revision() else: rev_spec = RevisionSpec.from_string(onto) onto = rev_spec.as_revision_id(upstream) wt.branch.repository.fetch(upstream_repository, onto) if stop_revid is None: stop_revid = wt.branch.last_revision() repo_graph = wt.branch.repository.get_graph() our_new, onto_unique = repo_graph.find_difference(stop_revid, onto) if start_revid is None: if not onto_unique: self.outf.write(gettext("No revisions to rebase.\n")) return if not our_new: self.outf.write(gettext("Base branch is descendant of current " "branch. Pulling instead.\n")) if not dry_run: wt.pull(upstream, onto) return # else: include extra revisions needed to make start_revid mean # something. # Create plan replace_map = generate_simple_plan( our_new, start_revid, stop_revid, onto, repo_graph, lambda revid, ps: regenerate_default_revid(wt.branch.repository, revid), not always_rebase_merges, ) if verbose or dry_run: todo = list(rebase_todo(wt.branch.repository, replace_map)) note(gettext("%d revisions will be rebased:") % len(todo)) for revid in todo: note("%s" % revid) if not dry_run: # Write plan file state.write_plan(replace_map) replayer = WorkingTreeRevisionRewriter(wt, state, merge_type=merge_type) finish_rebase(state, wt, replace_map, replayer) finally: wt.unlock()