# Copyright (C) 2006-2007 by Jelmer Vernooij
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""Tests for the rebase code."""

from ....conflicts import ConflictList
from ....errors import ConflictsInTree, UnknownFormatError
from ....graph import DictParentsProvider, Graph
from ....revision import NULL_REVISION
from ....tests import TestCase, TestCaseWithTransport
from ....tests.matchers import RevisionHistoryMatches
from ....transport import NoSuchFile
from ..rebase import (REBASE_CURRENT_REVID_FILENAME, REBASE_PLAN_FILENAME,
                      CommitBuilderRevisionRewriter, RebaseState1,
                      ReplaySnapshotError, WorkingTreeRevisionRewriter,
                      generate_simple_plan, generate_transpose_plan,
                      marshall_rebase_plan, rebase_todo,
                      unmarshall_rebase_plan)


class RebasePlanReadWriterTests(TestCase):

    def test_simple_marshall_rebase_plan(self):
        self.assertEqualDiff(b"""\
# Bazaar rebase plan 1
1 bla
oldrev newrev newparent1 newparent2
""", marshall_rebase_plan((1, b"bla"),
                          {b"oldrev": (b"newrev", (b"newparent1", b"newparent2"))}))

    def test_simple_unmarshall_rebase_plan(self):
        self.assertEqual(((1, b"bla"),
                          {b"oldrev": (b"newrev", (b"newparent1", b"newparent2"))}),
                          unmarshall_rebase_plan(b"""# Bazaar rebase plan 1
1 bla
oldrev newrev newparent1 newparent2
"""))

    def test_unmarshall_rebase_plan_formatunknown(self):
        self.assertRaises(UnknownFormatError,
                          unmarshall_rebase_plan, b"""# Bazaar rebase plan x
1 bla
oldrev newrev newparent1 newparent2
""")


class ConversionTests(TestCaseWithTransport):

    def test_simple(self):
        wt = self.make_branch_and_tree('.')
        b = wt.branch
        with open('hello', 'w') as f:
            f.write('hello world')
        wt.add('hello')
        wt.commit(message='add hello', rev_id=b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bloe")
        wt.set_last_revision(b"bla")
        b.generate_revision_history(b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bla2")

        wt.branch.repository.lock_write()
        newrev = CommitBuilderRevisionRewriter(wt.branch.repository)(
            b"bla2", b"bla4", (b"bloe",))
        self.assertEqual(b"bla4", newrev)
        self.assertTrue(wt.branch.repository.has_revision(newrev))
        self.assertEqual(
            (b"bloe",), wt.branch.repository.get_parent_map([newrev])[newrev])
        self.assertEqual(
            "bla2", wt.branch.repository.get_revision(newrev).properties["rebase-of"])
        wt.branch.repository.unlock()


class PlanCreatorTests(TestCaseWithTransport):

    def test_simple_plan_creator(self):
        wt = self.make_branch_and_tree('.')
        b = wt.branch
        with open('hello', 'w') as f:
            f.write('hello world')
        wt.add('hello')
        wt.commit(message='add hello', rev_id=b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bloe")
        wt.set_last_revision(b"bla")
        b.generate_revision_history(b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bla2")

        b.repository.lock_read()
        graph = b.repository.get_graph()
        self.assertEqual(
            {b'bla2': (b'newbla2', (b"bloe",))},
            generate_simple_plan(
                graph.find_difference(b.last_revision(), b"bla")[0],
                b"bla2", None, b"bloe", graph, lambda y, _: b"new" + y))
        b.repository.unlock()

    def test_simple_plan_creator_extra_history(self):
        wt = self.make_branch_and_tree('.')
        b = wt.branch
        with open('hello', 'w') as f:
            f.write('hello world')
        wt.add('hello')
        wt.commit(message='add hello', rev_id=b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bloe")
        wt.set_last_revision(b"bla")
        b.generate_revision_history(b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bla2")
        with open('hello', 'w') as f:
            f.write('universe')
        wt.commit(message='change hello again', rev_id=b"bla3")

        with b.repository.lock_read():
            graph = b.repository.get_graph()
            self.assertEqual(
                {b'bla2': (b'newbla2', (b"bloe",)), b'bla3': (b'newbla3', (b'newbla2',))},
                generate_simple_plan(
                    graph.find_difference(b.last_revision(), b"bloe")[0],
                    b"bla2", None, b"bloe",
                    graph, lambda y, _: b"new" + y))

    def test_generate_transpose_plan(self):
        wt = self.make_branch_and_tree('.')
        b = wt.branch
        with open('hello', 'w') as f:
            f.write('hello world')
        wt.add('hello')
        wt.commit(message='add hello', rev_id=b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bloe")
        wt.set_last_revision(b"bla")
        b.generate_revision_history(b"bla")
        with open('hello', 'w') as f:
            f.write('world')
        wt.commit(message='change hello', rev_id=b"bla2")
        with open('hello', 'w') as f:
            f.write('universe')
        wt.commit(message='change hello again', rev_id=b"bla3")
        wt.set_last_revision(b"bla")
        b.generate_revision_history(b"bla")
        with open('hello', 'w') as f:
            f.write('somebar')
        wt.commit(message='change hello yet again', rev_id=b"blie")
        wt.set_last_revision(NULL_REVISION)
        b.generate_revision_history(NULL_REVISION)
        wt.add('hello')
        wt.commit(message='add hello', rev_id=b"lala")

        b.repository.lock_read()
        graph = b.repository.get_graph()
        self.assertEqual({
            b'blie': (b'newblie', (b'lala',))},
            generate_transpose_plan(graph.iter_ancestry(
                [b"blie"]),
                {b"bla": b"lala"}, graph, lambda y, _: b"new" + y))
        self.assertEqual({
            b'bla2': (b'newbla2', (b'lala',)),
            b'bla3': (b'newbla3', (b'newbla2',)),
            b'blie': (b'newblie', (b'lala',)),
            b'bloe': (b'newbloe', (b'lala',))},
            generate_transpose_plan(graph.iter_ancestry(
                b.repository._all_revision_ids()),
                {b"bla": b"lala"}, graph, lambda y, _: b"new" + y))
        b.repository.unlock()

    def test_generate_transpose_plan_one(self):
        graph = Graph(DictParentsProvider({"bla": ("bloe",), "bloe": (), "lala": ()}))
        self.assertEqual(
            {"bla": ("newbla", ("lala",))},
            generate_transpose_plan(graph.iter_ancestry(
                ["bla", "bloe"]), {"bloe": "lala"}, graph, lambda y, _: "new" + y))

    def test_plan_with_already_merged(self):
        r"""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 -> E

        with a plan of:

        D -> (D', [C])
        E -> (E', [D', C])
        """
        parents_map = {
            "A": (),
            "B": ("A",),
            "C": ("B",),
            "D": ("A",),
            "E": ("D", "B")
        }
        graph = Graph(DictParentsProvider(parents_map))
        self.assertEqual(
            {"D": ("D'", ("C",)), "E": ("E'", ("D'",))},
            generate_simple_plan(
                ["D", "E"], "D", None, "C", graph, lambda y, _: y + "'"))

    def test_plan_with_already_merged_skip_merges(self):
        r"""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.assertEqual(
            {"D": ("D'", ("C",))},
            generate_simple_plan(
                ["D", "E"], "D", None, "C", graph, lambda y, _: y + "'", True))


class RebaseStateTests(TestCaseWithTransport):

    def setUp(self):
        super().setUp()
        self.wt = self.make_branch_and_tree('.')
        self.state = RebaseState1(self.wt)

    def test_rebase_plan_exists_false(self):
        self.assertFalse(self.state.has_plan())

    def test_rebase_plan_exists_empty(self):
        self.wt._transport.put_bytes(REBASE_PLAN_FILENAME, b"")
        self.assertFalse(self.state.has_plan())

    def test_rebase_plan_exists(self):
        self.wt._transport.put_bytes(REBASE_PLAN_FILENAME, b"foo")
        self.assertTrue(self.state.has_plan())

    def test_remove_rebase_plan(self):
        self.wt._transport.put_bytes(REBASE_PLAN_FILENAME, b"foo")
        self.state.remove_plan()
        self.assertFalse(self.state.has_plan())

    def test_remove_rebase_plan_twice(self):
        self.state.remove_plan()
        self.assertFalse(self.state.has_plan())

    def test_write_rebase_plan(self):
        with open('hello', 'w') as f:
            f.write('hello world')
        self.wt.add('hello')
        self.wt.commit(message='add hello', rev_id=b"bla")
        self.state.write_plan(
            {b"oldrev": (b"newrev", [b"newparent1", b"newparent2"])})
        self.assertEqualDiff(b"""# Bazaar rebase plan 1
1 bla
oldrev newrev newparent1 newparent2
""", self.wt._transport.get_bytes(REBASE_PLAN_FILENAME))

    def test_read_rebase_plan_nonexistant(self):
        self.assertRaises(NoSuchFile, self.state.read_plan)

    def test_read_rebase_plan_empty(self):
        self.wt._transport.put_bytes(REBASE_PLAN_FILENAME, b"")
        self.assertRaises(NoSuchFile, self.state.read_plan)

    def test_read_rebase_plan(self):
        self.wt._transport.put_bytes(
            REBASE_PLAN_FILENAME,
            b"""# Bazaar rebase plan 1
1 bla
oldrev newrev newparent1 newparent2
""")
        self.assertEqual(
            ((1, b"bla"), {b"oldrev": (b"newrev", (b"newparent1", b"newparent2"))}),
            self.state.read_plan())

    def test_read_nonexistant(self):
        self.assertIs(None, self.state.read_active_revid())

    def test_read_null(self):
        self.wt._transport.put_bytes(REBASE_CURRENT_REVID_FILENAME, NULL_REVISION)
        self.assertIs(None, self.state.read_active_revid())

    def test_read(self):
        self.wt._transport.put_bytes(REBASE_CURRENT_REVID_FILENAME, b"bla")
        self.assertEqual(b"bla", self.state.read_active_revid())

    def test_write(self):
        self.state.write_active_revid(b"bloe")
        self.assertEqual(b"bloe", self.state.read_active_revid())

    def test_write_null(self):
        self.state.write_active_revid(None)
        self.assertIs(None, self.state.read_active_revid())


class RebaseTodoTests(TestCase):

    def test_done(self):
        class Repository:
            def has_revision(self, revid):
                return revid == "bloe"
        self.assertEqual(
            [], list(rebase_todo(Repository(), {"bla": ("bloe", [])})))

    def test_notstarted(self):
        class Repository:
            def has_revision(self, revid):
                return False
        self.assertEqual(["bla"], list(rebase_todo(Repository(), {"bla": ("bloe", [])})))

    def test_halfway(self):
        class Repository:
            def has_revision(self, revid):
                return revid == "bloe"
        self.assertEqual(
            ["ha"],
            list(rebase_todo(Repository(), {"bla": ("bloe", []), "ha": ("hee", [])})))


class ReplaySnapshotTests(TestCaseWithTransport):

    def test_single_revision(self):
        wt = self.make_branch_and_tree(".")
        self.build_tree(['afile'])
        wt.add(["afile"])
        wt.commit("bla", rev_id=b"oldcommit")
        wt.branch.repository.lock_write()
        CommitBuilderRevisionRewriter(wt.branch.repository)(b"oldcommit", b"newcommit", ())
        wt.branch.repository.unlock()
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.committer, newrev.committer)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)
        tree = wt.branch.repository.revision_tree(b"newcommit")
        self.assertEqual(b"newcommit", tree.get_file_revision("afile"))

    def test_two_revisions(self):
        wt = self.make_branch_and_tree("old")
        self.build_tree_contents([('old/afile', 'afilecontents'), ('old/notherfile', 'notherfilecontents')])
        wt.add(["afile"], ids=[b"somefileid"])
        wt.commit("bla", rev_id=b"oldparent")
        wt.add(["notherfile"])
        wt.commit("bla", rev_id=b"oldcommit")
        oldrepos = wt.branch.repository
        wt = self.make_branch_and_tree("new")
        self.build_tree_contents([('new/afile', 'afilecontents'), ('new/notherfile', 'notherfilecontents')])
        wt.add(["afile"], ids=[b"afileid"])
        wt.commit("bla", rev_id=b"newparent")
        wt.branch.repository.fetch(oldrepos)
        wt.branch.repository.lock_write()
        CommitBuilderRevisionRewriter(wt.branch.repository)(
            b"oldcommit", b"newcommit", (b"newparent",))
        wt.branch.repository.unlock()
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([b"newparent"], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.committer, newrev.committer)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)
        tree = wt.branch.repository.revision_tree(b"newcommit")
        self.assertEqual(b"afileid", tree.path2id("afile"))
        self.assertEqual(b"newcommit", tree.get_file_revision("notherfile"))

    def test_two_revisions_no_renames(self):
        wt = self.make_branch_and_tree("old")
        self.build_tree(['old/afile', 'old/notherfile'])
        wt.add(["afile"], ids=[b"somefileid"])
        wt.commit("bla", rev_id=b"oldparent")
        wt.add(["notherfile"])
        wt.commit("bla", rev_id=b"oldcommit")
        oldrepos = wt.branch.repository
        wt = self.make_branch_and_tree("new")
        self.build_tree(['new/afile', 'new/notherfile'])
        wt.add(["afile"], ids=[b"afileid"])
        wt.commit("bla", rev_id=b"newparent")
        wt.branch.repository.fetch(oldrepos)
        wt.branch.repository.lock_write()
        CommitBuilderRevisionRewriter(wt.branch.repository)(
            b"oldcommit", b"newcommit", (b"newparent",))
        wt.branch.repository.unlock()

    def test_multi_revisions(self):
        wt = self.make_branch_and_tree("old")
        self.build_tree_contents(
            [('old/afile', 'afilecontent'), ('old/sfile', 'sfilecontent'), ('old/notherfile', 'notherfilecontent')])
        wt.add(['sfile'])
        wt.add(["afile"], ids=[b"somefileid"])
        wt.commit("bla", rev_id=b"oldgrandparent")
        with open("old/afile", "w") as f:
            f.write("data")
        wt.commit("bla", rev_id=b"oldparent")
        wt.add(["notherfile"])
        wt.commit("bla", rev_id=b"oldcommit")
        oldrepos = wt.branch.repository
        wt = self.make_branch_and_tree("new")
        self.build_tree_contents(
            [('new/afile', 'afilecontent'), ('new/sfile', 'sfilecontent'), ('new/notherfile', 'notherfilecontent')])
        wt.add(['sfile'])
        wt.add(["afile"], ids=[b"afileid"])
        wt.commit("bla", rev_id=b"newgrandparent")
        with open("new/afile", "w") as f:
            f.write("data")
        wt.commit("bla", rev_id=b"newparent")
        wt.branch.repository.fetch(oldrepos)
        wt.branch.repository.lock_write()
        CommitBuilderRevisionRewriter(wt.branch.repository)(
            b"oldcommit", b"newcommit", (b"newparent",))
        wt.branch.repository.unlock()
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([b"newparent"], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.committer, newrev.committer)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)
        tree = wt.branch.repository.revision_tree(b"newcommit")
        self.assertEqual(b"afileid", tree.path2id("afile"))
        self.assertEqual(b"newcommit", tree.get_file_revision("notherfile"))
        self.assertEqual(b"newgrandparent", tree.get_file_revision("sfile"))

    def test_maps_ids(self):
        wt = self.make_branch_and_tree("old")
        wt.commit("base", rev_id=b"base")
        self.build_tree(['old/afile'])
        wt.add(["afile"], ids=[b"originalid"])
        wt.commit("bla", rev_id=b"oldparent")
        with open("old/afile", "w") as f:
            f.write("bloe")
        wt.commit("bla", rev_id=b"oldcommit")
        oldrepos = wt.branch.repository
        wt = self.make_branch_and_tree("new")
        self.build_tree(['new/afile'])
        wt.add(["afile"], ids=[b"newid"])
        wt.commit("bla", rev_id=b"newparent")
        wt.branch.repository.fetch(oldrepos)
        wt.branch.repository.lock_write()
        CommitBuilderRevisionRewriter(wt.branch.repository)(
            b"oldcommit", b"newcommit", (b"newparent",))
        wt.branch.repository.unlock()
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([b"newparent"], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.committer, newrev.committer)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)
        tree = wt.branch.repository.revision_tree(b"newcommit")
        self.assertEqual(b"newid", tree.path2id("afile"))
        self.assertEqual(b"newcommit", tree.get_file_revision("afile"))


class TestReplayWorkingtree(TestCaseWithTransport):
    def test_conflicts(self):
        wt = self.make_branch_and_tree("old")
        wt.commit("base", rev_id=b"base")
        self.build_tree(['old/afile'])
        wt.add(["afile"], ids=[b"originalid"])
        wt.commit("bla", rev_id=b"oldparent")
        with open("old/afile", "w") as f:
            f.write("bloe")
        wt.commit("bla", rev_id=b"oldcommit")
        oldrepos = wt.branch.repository
        wt = self.make_branch_and_tree("new")
        self.build_tree(['new/afile'])
        wt.add(["afile"], ids=[b"newid"])
        wt.commit("bla", rev_id=b"newparent")
        wt.branch.repository.fetch(oldrepos)
        with wt.lock_write():
            replayer = WorkingTreeRevisionRewriter(wt, RebaseState1(wt))
            self.assertRaises(
                ConflictsInTree,
                replayer, b"oldcommit", b"newcommit",
                [b"newparent"], )

    def test_simple(self):
        wt = self.make_branch_and_tree("old")
        wt.commit("base", rev_id=b"base")
        self.build_tree(['old/afile'])
        wt.add(["afile"], ids=[b"originalid"])
        wt.commit("bla", rev_id=b"oldparent")
        with open("old/afile", "w") as f:
            f.write("bloe")
        wt.commit("bla", rev_id=b"oldcommit")
        wt = wt.controldir.sprout("new").open_workingtree()
        self.build_tree(['new/bfile'])
        wt.add(["bfile"], ids=[b"newid"])
        wt.commit("bla", rev_id=b"newparent")
        replayer = WorkingTreeRevisionRewriter(wt, RebaseState1(wt))
        replayer(b"oldcommit", b"newcommit", [b"newparent"])
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([b"newparent"], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)

    def test_multiple(self):
        # rebase from
        # base: []
        # oldparent: [base]
        # newparent: [base]
        # oldcommit: [oldparent, ghost]
        # create newcommit by rebasing oldcommit from oldparent to newparent,
        # keeping the merge of ghost.
        # Common base:
        wt = self.make_branch_and_tree("old")
        wt.commit("base", rev_id=b"base")
        # oldparent:
        self.build_tree_contents([('old/afile', 'base content')])
        wt.add(["afile"], ids=[b"originalid"])
        wt.commit("bla", rev_id=b"oldparent")
        # oldcommit (the delta getting rebased)
        #  - change the content of afile to be 'bloe'
        with open("old/afile", "w") as f:
            f.write("bloe")
        wt.add_pending_merge(b"ghost")
        wt.commit("bla", rev_id=b"oldcommit")
        # newparent (the new base for the rebased commit)
        new_tree = wt.controldir.sprout(
            "new", revision_id=b'base').open_workingtree()
        new_tree.branch.repository.fetch(wt.branch.repository)
        wt = new_tree
        self.build_tree_contents([('new/afile', 'base content')])
        wt.add(["afile"], ids=[b"originalid"])
        wt.commit("bla", rev_id=b"newparent")
        # And do it!
        wt.lock_write()
        replayer = WorkingTreeRevisionRewriter(wt, RebaseState1(wt))
        replayer(b"oldcommit", b"newcommit", (b"newparent", b"ghost"))
        wt.unlock()
        oldrev = wt.branch.repository.get_revision(b"oldcommit")
        newrev = wt.branch.repository.get_revision(b"newcommit")
        self.assertEqual([b"oldparent", b"ghost"], oldrev.parent_ids)
        self.assertEqual([b"newparent", b"ghost"], newrev.parent_ids)
        self.assertEqual(b"newcommit", newrev.revision_id)
        self.assertEqual(oldrev.timestamp, newrev.timestamp)
        self.assertEqual(oldrev.timezone, newrev.timezone)

    def test_already_merged(self):
        r"""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' -> E'

        Ancestry:
        A:
        B: A
        C: A, B
        D: A
        E: A, B, D
        D': A, B, C
        E': A, B, C, D'

        """
        oldwt = self.make_branch_and_tree("old")
        self.build_tree(['old/afile'])
        with open("old/afile", "w") as f:
            f.write("A\n" * 10)
        oldwt.add(["afile"])
        oldwt.commit("base", rev_id=b"A")
        newwt = oldwt.controldir.sprout("new").open_workingtree()
        with open("old/afile", "w") as f:
            f.write("A\n" * 10 + "B\n")
        oldwt.commit("bla", rev_id=b"B")
        with open("old/afile", "w") as f:
            f.write("A\n" * 10 + "C\n")
        oldwt.commit("bla", rev_id=b"C")
        self.build_tree(['new/bfile'])
        newwt.add(["bfile"])
        with open("new/bfile", "w") as f:
            f.write("D\n")
        newwt.commit("bla", rev_id=b"D")
        with open("new/afile", "w") as f:
            f.write("E\n" + "A\n" * 10 + "B\n")
        with open("new/bfile", "w") as f:
            f.write("D\nE\n")
        newwt.add_pending_merge(b"B")
        newwt.commit("bla", rev_id=b"E")
        newwt.branch.repository.fetch(oldwt.branch.repository)
        newwt.lock_write()
        replayer = WorkingTreeRevisionRewriter(newwt, RebaseState1(newwt))
        replayer(b"D", b"D'", [b"C"])
        newwt.unlock()
        oldrev = newwt.branch.repository.get_revision(b"D")
        newrev = newwt.branch.repository.get_revision(b"D'")
        self.assertEqual([b"C"], newrev.parent_ids)
        newwt.lock_write()
        replayer = WorkingTreeRevisionRewriter(newwt, RebaseState1(newwt))
        self.assertRaises(ConflictsInTree, replayer, b"E", b"E'", [b"D'"])
        newwt.unlock()
        with open("new/afile") as f:
            self.assertEqual("E\n" + "A\n" * 10 + "C\n", f.read())
        newwt.set_conflicts([])
        oldrev = newwt.branch.repository.get_revision(b"E")
        replayer.commit_rebase(oldrev, b"E'")
        newrev = newwt.branch.repository.get_revision(b"E'")
        self.assertEqual([b"D'"], newrev.parent_ids)
        self.assertThat(
            newwt.branch,
            RevisionHistoryMatches([b"A", b"B", b"C", b"D'", b"E'"]))


class TestReplaySnapshotError(TestCase):

    def test_create(self):
        ReplaySnapshotError("message")
