Пример #1
0
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
                   accelerator_tree=None, hardlink=False):
        """See WorkingTreeFormat.initialize().

        :param revision_id: if supplied, create a working tree at a different
            revision than the branch is at.
        :param accelerator_tree: A tree which can be used for retrieving file
            contents more quickly than the revision tree, i.e. a workingtree.
            The revision tree will be used for cases where accelerator_tree's
            content is different.
        :param hardlink: If true, hard-link files from accelerator_tree,
            where possible.
        """
        if not isinstance(a_bzrdir.transport, LocalTransport):
            raise errors.NotLocalUrl(a_bzrdir.transport.base)
        transport = a_bzrdir.get_workingtree_transport(self)
        control_files = self._open_control_files(a_bzrdir)
        control_files.create_lock()
        control_files.lock_write()
        transport.put_bytes('format', self.as_string(),
            mode=a_bzrdir._get_file_mode())
        if from_branch is not None:
            branch = from_branch
        else:
            branch = a_bzrdir.open_branch()
        if revision_id is None:
            revision_id = _mod_revision.ensure_null(branch.last_revision())
        # WorkingTree3 can handle an inventory which has a unique root id.
        # as of bzr 0.12. However, bzr 0.11 and earlier fail to handle
        # those trees. And because there isn't a format bump inbetween, we
        # are maintaining compatibility with older clients.
        # inv = Inventory(root_id=gen_root_id())
        inv = self._initial_inventory()
        wt = self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
                         branch,
                         inv,
                         _internal=True,
                         _format=self,
                         _bzrdir=a_bzrdir,
                         _control_files=control_files)
        wt.lock_tree_write()
        try:
            basis_tree = branch.repository.revision_tree(revision_id)
            # only set an explicit root id if there is one to set.
            if basis_tree.get_root_id() is not None:
                wt.set_root_id(basis_tree.get_root_id())
            if revision_id == _mod_revision.NULL_REVISION:
                wt.set_parent_trees([])
            else:
                wt.set_parent_trees([(revision_id, basis_tree)])
            transform.build_tree(basis_tree, wt)
            for hook in MutableTree.hooks['post_build_tree']:
                hook(wt)
        finally:
            # Unlock in this order so that the unlock-triggers-flush in
            # WorkingTree is given a chance to fire.
            control_files.unlock()
            wt.unlock()
        return wt
Пример #2
0
    def revision_tree(self, revision_id):
        revision_id = revision.ensure_null(revision_id)

        if revision_id == revision.NULL_REVISION:
            inv = inventory.Inventory(root_id=None)
            inv.revision_id = revision_id
            return revisiontree.RevisionTree(self, inv, revision_id)

        return GitRevisionTree(self, self.get_mapping(), revision_id)
Пример #3
0
    def revision_tree(self, revision_id):
        revision_id = revision.ensure_null(revision_id)

        if revision_id == revision.NULL_REVISION:
            inv = inventory.Inventory(root_id=None)
            inv.revision_id = revision_id
            return revisiontree.RevisionTree(self, inv, revision_id)

        return GitRevisionTree(self, self.get_mapping(), revision_id)
Пример #4
0
 def _as_revision_id(self, context_branch):
     from bzrlib.branch import Branch
     other_branch = Branch.open(self.spec)
     last_revision = other_branch.last_revision()
     last_revision = revision.ensure_null(last_revision)
     context_branch.fetch(other_branch, last_revision)
     if last_revision == revision.NULL_REVISION:
         raise errors.NoCommits(other_branch)
     return last_revision
Пример #5
0
 def make_read_requests(self, branch):
     """Do some read only requests."""
     branch.lock_read()
     try:
         branch.repository.all_revision_ids()
         self.assertEqual(_mod_revision.NULL_REVISION,
                          _mod_revision.ensure_null(branch.last_revision()))
     finally:
         branch.unlock()
Пример #6
0
 def make_read_requests(self, branch):
     """Do some read only requests."""
     branch.lock_read()
     try:
         branch.repository.all_revision_ids()
         self.assertEqual(_mod_revision.NULL_REVISION,
                          _mod_revision.ensure_null(branch.last_revision()))
     finally:
         branch.unlock()
Пример #7
0
 def set_last_revision_info(self, revno, revision_id):
     if not revision_id or not isinstance(revision_id, basestring):
         raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
     revision_id = _mod_revision.ensure_null(revision_id)
     # this old format stores the full history, but this api doesn't
     # provide it, so we must generate, and might as well check it's
     # correct
     history = self._lefthand_history(revision_id)
     if len(history) != revno:
         raise AssertionError('%d != %d' % (len(history), revno))
     self._set_revision_history(history)
Пример #8
0
    def from_objects(klass, repository, revision_id, time, timezone,
                 target_branch, patch_type='bundle',
                 local_target_branch=None, public_branch=None, message=None):
        """Generate a merge directive from various objects

        :param repository: The repository containing the revision
        :param revision_id: The revision to merge
        :param time: The POSIX timestamp of the date the request was issued.
        :param timezone: The timezone of the request
        :param target_branch: The url of the branch to merge into
        :param patch_type: 'bundle', 'diff' or None, depending on the type of
            patch desired.
        :param local_target_branch: a local copy of the target branch
        :param public_branch: location of a public branch containing the target
            revision.
        :param message: Message to use when committing the merge
        :return: The merge directive

        The public branch is always used if supplied.  If the patch_type is
        not 'bundle', the public branch must be supplied, and will be verified.

        If the message is not supplied, the message from revision_id will be
        used for the commit.
        """
        t_revision_id = revision_id
        if revision_id == _mod_revision.NULL_REVISION:
            t_revision_id = None
        t = testament.StrictTestament3.from_revision(repository, t_revision_id)
        submit_branch = _mod_branch.Branch.open(target_branch)
        if submit_branch.get_public_branch() is not None:
            target_branch = submit_branch.get_public_branch()
        if patch_type is None:
            patch = None
        else:
            submit_revision_id = submit_branch.last_revision()
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
            repository.fetch(submit_branch.repository, submit_revision_id)
            graph = repository.get_graph()
            ancestor_id = graph.find_unique_lca(revision_id,
                                                submit_revision_id)
            type_handler = {'bundle': klass._generate_bundle,
                            'diff': klass._generate_diff,
                            None: lambda x, y, z: None }
            patch = type_handler[patch_type](repository, revision_id,
                                             ancestor_id)

        if public_branch is not None and patch_type != 'bundle':
            public_branch_obj = _mod_branch.Branch.open(public_branch)
            if not public_branch_obj.repository.has_revision(revision_id):
                raise errors.PublicBranchOutOfDate(public_branch,
                                                   revision_id)

        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
            patch, patch_type, public_branch, message)
Пример #9
0
    def assertConsistentParents(self, expected, tree):
        """Check that the parents found are as expected.

        This test helper also checks that they are consistent with
        the pre-get_parent_ids() api - which is now deprecated.
        """
        self.assertEqual(expected, tree.get_parent_ids())
        if expected == []:
            self.assertEqual(_mod_revision.NULL_REVISION,
                             _mod_revision.ensure_null(tree.last_revision()))
        else:
            self.assertEqual(expected[0], tree.last_revision())
Пример #10
0
    def assertConsistentParents(self, expected, tree):
        """Check that the parents found are as expected.

        This test helper also checks that they are consistent with
        the pre-get_parent_ids() api - which is now deprecated.
        """
        self.assertEqual(expected, tree.get_parent_ids())
        if expected == []:
            self.assertEqual(_mod_revision.NULL_REVISION,
                             _mod_revision.ensure_null(tree.last_revision()))
        else:
            self.assertEqual(expected[0], tree.last_revision())
Пример #11
0
 def set_last_revision_info(self, revno, revision_id):
     if not revision_id or not isinstance(revision_id, basestring):
         raise errors.InvalidRevisionId(revision_id=revision_id,
                                        branch=self)
     revision_id = _mod_revision.ensure_null(revision_id)
     # this old format stores the full history, but this api doesn't
     # provide it, so we must generate, and might as well check it's
     # correct
     history = self._lefthand_history(revision_id)
     if len(history) != revno:
         raise AssertionError('%d != %d' % (len(history), revno))
     self._set_revision_history(history)
Пример #12
0
    def _find_revision_id(branch, other_location):
        from bzrlib.branch import Branch

        branch.lock_read()
        try:
            revision_a = revision.ensure_null(branch.last_revision())
            if revision_a == revision.NULL_REVISION:
                raise errors.NoCommits(branch)
            other_branch = Branch.open(other_location)
            other_branch.lock_read()
            try:
                revision_b = revision.ensure_null(other_branch.last_revision())
                if revision_b == revision.NULL_REVISION:
                    raise errors.NoCommits(other_branch)
                graph = branch.repository.get_graph(other_branch.repository)
                rev_id = graph.find_unique_lca(revision_a, revision_b)
            finally:
                other_branch.unlock()
            if rev_id == revision.NULL_REVISION:
                raise errors.NoCommonAncestor(revision_a, revision_b)
            return rev_id
        finally:
            branch.unlock()
Пример #13
0
 def test_local_commit_does_not_push_to_master(self):
     # a --local commit does not require access to the master branch
     # at all, or even for it to exist.
     # we test that even when its available it does not push to it.
     master = self.make_branch('master')
     tree = self.make_branch_and_tree('tree')
     try:
         tree.branch.bind(master)
     except errors.UpgradeRequired:
         # older format.
         return
     tree.commit('foo', rev_id='foo', local=True)
     self.assertFalse(master.repository.has_revision('foo'))
     self.assertEqual(_mod_revision.NULL_REVISION,
                      (_mod_revision.ensure_null(master.last_revision())))
Пример #14
0
 def test_local_commit_does_not_push_to_master(self):
     # a --local commit does not require access to the master branch
     # at all, or even for it to exist.
     # we test that even when its available it does not push to it.
     master = self.make_branch('master')
     tree = self.make_branch_and_tree('tree')
     try:
         tree.branch.bind(master)
     except errors.UpgradeRequired:
         # older format.
         return
     tree.commit('foo', rev_id='foo', local=True)
     self.failIf(master.repository.has_revision('foo'))
     self.assertEqual(_mod_revision.NULL_REVISION,
                      (_mod_revision.ensure_null(master.last_revision())))
Пример #15
0
def _any_local_commits(this_branch, possible_transports):
    """Does this branch have any commits not in the master branch?"""
    last_rev = revision.ensure_null(this_branch.last_revision())
    if last_rev != revision.NULL_REVISION:
        other_branch = this_branch.get_master_branch(possible_transports)
        this_branch.lock_read()
        other_branch.lock_read()
        try:
            other_last_rev = other_branch.last_revision()
            graph = this_branch.repository.get_graph(other_branch.repository)
            if not graph.is_ancestor(last_rev, other_last_rev):
                return True
        finally:
            other_branch.unlock()
            this_branch.unlock()
    return False
Пример #16
0
def _any_local_commits(this_branch, possible_transports):
    """Does this branch have any commits not in the master branch?"""
    last_rev = revision.ensure_null(this_branch.last_revision())
    if last_rev != revision.NULL_REVISION:
        other_branch = this_branch.get_master_branch(possible_transports)
        this_branch.lock_read()
        other_branch.lock_read()
        try:
            other_last_rev = other_branch.last_revision()
            graph = this_branch.repository.get_graph(
                other_branch.repository)
            if not graph.is_ancestor(last_rev, other_last_rev):
                return True
        finally:
            other_branch.unlock()
            this_branch.unlock()
    return False
Пример #17
0
 def initialize(self,
                a_bzrdir,
                revision_id=None,
                from_branch=None,
                accelerator_tree=None,
                hardlink=False):
     """See WorkingTreeFormat.initialize()."""
     if not isinstance(a_bzrdir.transport, LocalTransport):
         raise errors.NotLocalUrl(a_bzrdir.transport.base)
     if from_branch is not None:
         branch = from_branch
     else:
         branch = a_bzrdir.open_branch()
     if revision_id is None:
         revision_id = _mod_revision.ensure_null(branch.last_revision())
     branch.lock_write()
     try:
         branch.generate_revision_history(revision_id)
     finally:
         branch.unlock()
     inv = inventory.Inventory()
     wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
                       branch,
                       inv,
                       _internal=True,
                       _format=self,
                       _bzrdir=a_bzrdir,
                       _control_files=branch.control_files)
     basis_tree = branch.repository.revision_tree(revision_id)
     if basis_tree.get_root_id() is not None:
         wt.set_root_id(basis_tree.get_root_id())
     # set the parent list and cache the basis tree.
     if _mod_revision.is_null(revision_id):
         parent_trees = []
     else:
         parent_trees = [(revision_id, basis_tree)]
     wt.set_parent_trees(parent_trees)
     transform.build_tree(basis_tree, wt)
     for hook in MutableTree.hooks['post_build_tree']:
         hook(wt)
     return wt
Пример #18
0
 def test_sprout_bzrdir_repository(self):
     tree = self.make_branch_and_tree('commit_tree')
     self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
     tree.add('foo')
     tree.commit('revision 1', rev_id='1')
     dir = self.make_bzrdir('source')
     repo = dir.create_repository()
     repo.fetch(tree.branch.repository)
     self.assertTrue(repo.has_revision('1'))
     try:
         self.assertTrue(
             _mod_revision.is_null(_mod_revision.ensure_null(
             dir.open_branch().last_revision())))
     except errors.NotBranchError:
         pass
     target = dir.sprout(self.get_url('target'))
     self.assertNotEqual(dir.transport.base, target.transport.base)
     # testing inventory isn't reasonable for repositories
     self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
                                 [
                                  './.bzr/branch',
                                  './.bzr/checkout',
                                  './.bzr/inventory',
                                  './.bzr/parent',
                                  './.bzr/repository/inventory.knit',
                                  ])
     try:
         local_inventory = dir.transport.local_abspath('inventory')
     except errors.NotLocalUrl:
         return
     try:
         # If we happen to have a tree, we'll guarantee everything
         # except for the tree root is the same.
         inventory_f = file(local_inventory, 'rb')
         self.addCleanup(inventory_f.close)
         self.assertContainsRe(inventory_f.read(),
                               '<inventory format="5">\n</inventory>\n')
     except IOError, e:
         if e.errno != errno.ENOENT:
             raise
Пример #19
0
 def test_sprout_bzrdir_repository(self):
     tree = self.make_branch_and_tree('commit_tree')
     self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
     tree.add('foo')
     tree.commit('revision 1', rev_id='1')
     dir = self.make_bzrdir('source')
     repo = dir.create_repository()
     repo.fetch(tree.branch.repository)
     self.assertTrue(repo.has_revision('1'))
     try:
         self.assertTrue(
             _mod_revision.is_null(_mod_revision.ensure_null(
             dir.open_branch().last_revision())))
     except errors.NotBranchError:
         pass
     target = dir.sprout(self.get_url('target'))
     self.assertNotEqual(dir.transport.base, target.transport.base)
     # testing inventory isn't reasonable for repositories
     self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
                                 [
                                  './.bzr/branch',
                                  './.bzr/checkout',
                                  './.bzr/inventory',
                                  './.bzr/parent',
                                  './.bzr/repository/inventory.knit',
                                  ])
     try:
         local_inventory = dir.transport.local_abspath('inventory')
     except errors.NotLocalUrl:
         return
     try:
         # If we happen to have a tree, we'll guarantee everything
         # except for the tree root is the same.
         inventory_f = file(local_inventory, 'rb')
         self.addCleanup(inventory_f.close)
         self.assertContainsRe(inventory_f.read(),
                               '<inventory format="5">\n</inventory>\n')
     except IOError, e:
         if e.errno != errno.ENOENT:
             raise
Пример #20
0
 def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
                accelerator_tree=None, hardlink=False):
     """See WorkingTreeFormat.initialize()."""
     if not isinstance(a_bzrdir.transport, LocalTransport):
         raise errors.NotLocalUrl(a_bzrdir.transport.base)
     if from_branch is not None:
         branch = from_branch
     else:
         branch = a_bzrdir.open_branch()
     if revision_id is None:
         revision_id = _mod_revision.ensure_null(branch.last_revision())
     branch.lock_write()
     try:
         branch.generate_revision_history(revision_id)
     finally:
         branch.unlock()
     inv = inventory.Inventory()
     wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
                      branch,
                      inv,
                      _internal=True,
                      _format=self,
                      _bzrdir=a_bzrdir,
                      _control_files=branch.control_files)
     basis_tree = branch.repository.revision_tree(revision_id)
     if basis_tree.get_root_id() is not None:
         wt.set_root_id(basis_tree.get_root_id())
     # set the parent list and cache the basis tree.
     if _mod_revision.is_null(revision_id):
         parent_trees = []
     else:
         parent_trees = [(revision_id, basis_tree)]
     wt.set_parent_trees(parent_trees)
     transform.build_tree(basis_tree, wt)
     for hook in MutableTree.hooks['post_build_tree']:
         hook(wt)
     return wt
Пример #21
0
 def test_update_unbound_works(self):
     b = self.make_branch('.')
     b.update()
     self.assertEqual(_mod_revision.NULL_REVISION,
                      _mod_revision.ensure_null(b.last_revision()))
Пример #22
0
 def test_update_unbound_works(self):
     b = self.make_branch('.')
     b.update()
     self.assertEqual(_mod_revision.NULL_REVISION,
                      _mod_revision.ensure_null(b.last_revision()))
Пример #23
0
    def from_objects(klass, repository, revision_id, time, timezone,
                 target_branch, include_patch=True, include_bundle=True,
                 local_target_branch=None, public_branch=None, message=None,
                 base_revision_id=None):
        """Generate a merge directive from various objects

        :param repository: The repository containing the revision
        :param revision_id: The revision to merge
        :param time: The POSIX timestamp of the date the request was issued.
        :param timezone: The timezone of the request
        :param target_branch: The url of the branch to merge into
        :param include_patch: If true, include a preview patch
        :param include_bundle: If true, include a bundle
        :param local_target_branch: a local copy of the target branch
        :param public_branch: location of a public branch containing the target
            revision.
        :param message: Message to use when committing the merge
        :return: The merge directive

        The public branch is always used if supplied.  If no bundle is
        included, the public branch must be supplied, and will be verified.

        If the message is not supplied, the message from revision_id will be
        used for the commit.
        """
        locked = []
        try:
            repository.lock_write()
            locked.append(repository)
            t_revision_id = revision_id
            if revision_id == 'null:':
                t_revision_id = None
            t = testament.StrictTestament3.from_revision(repository,
                t_revision_id)
            submit_branch = _mod_branch.Branch.open(target_branch)
            submit_branch.lock_read()
            locked.append(submit_branch)
            if submit_branch.get_public_branch() is not None:
                target_branch = submit_branch.get_public_branch()
            submit_revision_id = submit_branch.last_revision()
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
            graph = repository.get_graph(submit_branch.repository)
            ancestor_id = graph.find_unique_lca(revision_id,
                                                submit_revision_id)
            if base_revision_id is None:
                base_revision_id = ancestor_id
            if (include_patch, include_bundle) != (False, False):
                repository.fetch(submit_branch.repository, submit_revision_id)
            if include_patch:
                patch = klass._generate_diff(repository, revision_id,
                                             base_revision_id)
            else:
                patch = None

            if include_bundle:
                bundle = klass._generate_bundle(repository, revision_id,
                    ancestor_id).encode('base-64')
            else:
                bundle = None

            if public_branch is not None and not include_bundle:
                public_branch_obj = _mod_branch.Branch.open(public_branch)
                public_branch_obj.lock_read()
                locked.append(public_branch_obj)
                if not public_branch_obj.repository.has_revision(
                    revision_id):
                    raise errors.PublicBranchOutOfDate(public_branch,
                                                       revision_id)
        finally:
            for entry in reversed(locked):
                entry.unlock()
        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
            patch, public_branch, message, bundle, base_revision_id)
Пример #24
0
 def create_on_branch(branch):
     """Create a MemoryTree for branch, using the last-revision of branch."""
     revision_id = _mod_revision.ensure_null(branch.last_revision())
     return MemoryTree(branch, revision_id)
Пример #25
0
 def create_on_branch(branch):
     """Create a MemoryTree for branch, using the last-revision of branch."""
     revision_id = _mod_revision.ensure_null(branch.last_revision())
     return MemoryTree(branch, revision_id)
Пример #26
0
    def from_objects(klass,
                     repository,
                     revision_id,
                     time,
                     timezone,
                     target_branch,
                     include_patch=True,
                     include_bundle=True,
                     local_target_branch=None,
                     public_branch=None,
                     message=None,
                     base_revision_id=None):
        """Generate a merge directive from various objects

        :param repository: The repository containing the revision
        :param revision_id: The revision to merge
        :param time: The POSIX timestamp of the date the request was issued.
        :param timezone: The timezone of the request
        :param target_branch: The url of the branch to merge into
        :param include_patch: If true, include a preview patch
        :param include_bundle: If true, include a bundle
        :param local_target_branch: a local copy of the target branch
        :param public_branch: location of a public branch containing the target
            revision.
        :param message: Message to use when committing the merge
        :return: The merge directive

        The public branch is always used if supplied.  If no bundle is
        included, the public branch must be supplied, and will be verified.

        If the message is not supplied, the message from revision_id will be
        used for the commit.
        """
        locked = []
        try:
            repository.lock_write()
            locked.append(repository)
            t_revision_id = revision_id
            if revision_id == 'null:':
                t_revision_id = None
            t = testament.StrictTestament3.from_revision(
                repository, t_revision_id)
            submit_branch = _mod_branch.Branch.open(target_branch)
            submit_branch.lock_read()
            locked.append(submit_branch)
            if submit_branch.get_public_branch() is not None:
                target_branch = submit_branch.get_public_branch()
            submit_revision_id = submit_branch.last_revision()
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
            graph = repository.get_graph(submit_branch.repository)
            ancestor_id = graph.find_unique_lca(revision_id,
                                                submit_revision_id)
            if base_revision_id is None:
                base_revision_id = ancestor_id
            if (include_patch, include_bundle) != (False, False):
                repository.fetch(submit_branch.repository, submit_revision_id)
            if include_patch:
                patch = klass._generate_diff(repository, revision_id,
                                             base_revision_id)
            else:
                patch = None

            if include_bundle:
                bundle = klass._generate_bundle(repository, revision_id,
                                                ancestor_id).encode('base-64')
            else:
                bundle = None

            if public_branch is not None and not include_bundle:
                public_branch_obj = _mod_branch.Branch.open(public_branch)
                public_branch_obj.lock_read()
                locked.append(public_branch_obj)
                if not public_branch_obj.repository.has_revision(revision_id):
                    raise errors.PublicBranchOutOfDate(public_branch,
                                                       revision_id)
        finally:
            for entry in reversed(locked):
                entry.unlock()
        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
                     patch, public_branch, message, bundle, base_revision_id)
Пример #27
0
    def from_objects(klass,
                     repository,
                     revision_id,
                     time,
                     timezone,
                     target_branch,
                     patch_type='bundle',
                     local_target_branch=None,
                     public_branch=None,
                     message=None):
        """Generate a merge directive from various objects

        :param repository: The repository containing the revision
        :param revision_id: The revision to merge
        :param time: The POSIX timestamp of the date the request was issued.
        :param timezone: The timezone of the request
        :param target_branch: The url of the branch to merge into
        :param patch_type: 'bundle', 'diff' or None, depending on the type of
            patch desired.
        :param local_target_branch: a local copy of the target branch
        :param public_branch: location of a public branch containing the target
            revision.
        :param message: Message to use when committing the merge
        :return: The merge directive

        The public branch is always used if supplied.  If the patch_type is
        not 'bundle', the public branch must be supplied, and will be verified.

        If the message is not supplied, the message from revision_id will be
        used for the commit.
        """
        t_revision_id = revision_id
        if revision_id == _mod_revision.NULL_REVISION:
            t_revision_id = None
        t = testament.StrictTestament3.from_revision(repository, t_revision_id)
        submit_branch = _mod_branch.Branch.open(target_branch)
        if submit_branch.get_public_branch() is not None:
            target_branch = submit_branch.get_public_branch()
        if patch_type is None:
            patch = None
        else:
            submit_revision_id = submit_branch.last_revision()
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
            repository.fetch(submit_branch.repository, submit_revision_id)
            graph = repository.get_graph()
            ancestor_id = graph.find_unique_lca(revision_id,
                                                submit_revision_id)
            type_handler = {
                'bundle': klass._generate_bundle,
                'diff': klass._generate_diff,
                None: lambda x, y, z: None
            }
            patch = type_handler[patch_type](repository, revision_id,
                                             ancestor_id)

        if public_branch is not None and patch_type != 'bundle':
            public_branch_obj = _mod_branch.Branch.open(public_branch)
            if not public_branch_obj.repository.has_revision(revision_id):
                raise errors.PublicBranchOutOfDate(public_branch, revision_id)

        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
                     patch, patch_type, public_branch, message)