[MERGE] enhanced mv command

Marius Kruger amanic at gmail.com
Fri Dec 22 18:52:49 GMT 2006


hi,
thanks for the review John,
I incorporated you requests as best I could.

<http://www.megaupload.com/?d=BJMOGQ5Y>There is now special error classes
for
move/rename which takes in the
specialized exception as extra.

This might be  a little overkill,
but it is far more elegant with better messages,
than my attempt before that (you can see it
if you uncommit one revision before the last merge in this bundle)

I'm zipping the bundle again.
If anybody has an idea why my bundles are so big,
and how i could avoid it, please let me know.

I'll also attach the diff of my latest changes.
with all the merges I don't know how to isolate
the changes I made, so you'll have to check my
previous changes in the bundle or from my previous
post.

regards
marius


-- 



I code therefore I am.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.ubuntu.com/archives/bazaar/attachments/20061222/77261ec5/attachment.htm 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: enhancedmv3.patch.zip
Type: application/zip
Size: 273227 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20061222/77261ec5/attachment.zip 
-------------- next part --------------
=== modified file 'bzrlib/errors.py'
--- bzrlib/errors.py	2006-12-21 19:15:35 +0000
+++ bzrlib/errors.py	2006-12-22 18:22:50 +0000
@@ -18,7 +18,8 @@
 """
 
 
-from bzrlib import symbol_versioning
+from bzrlib import (symbol_versioning, 
+                    osutils,)
 from bzrlib.patches import (PatchSyntax, 
                             PatchConflict, 
                             MalformedPatchHeader,
@@ -320,21 +321,25 @@
 
 
 class FilesExist(PathError):
+    """Used when reporting that files do exist"""
     
-    _fmt = "File(s) exist: %(paths_as_string)s%(extra)s"
-
-    # used when reporting that files do exist
+    _fmt = "File%(plural)s exist: %(paths_as_string)s%(extra)s"
 
     def __init__(self, paths, extra=None):
         # circular import
         from bzrlib.osutils import quotefn
         BzrError.__init__(self)
         self.paths = paths
-        self.paths_as_string = ' '.join([quotefn(p) for p in paths])
+        self.paths_as_string = ' '.join(paths)
         if extra:
             self.extra = ': ' + str(extra)
         else:
             self.extra = ''
+        if len(paths) > 1:
+            self.plural = 's'
+        else:
+            self.plural = ''
+
 
 class NotADirectory(PathError):
 
@@ -519,49 +524,49 @@
 
 
 class AlreadyVersionedError(BzrError):
-    #Used when a path is expected not to be versioned, but it is.
+    """Used when a path is expected not to be versioned, but it is."""
     
-    _fmt = "%(contextInfo)s%(path)s is already versioned"
+    _fmt = "%(context_info)s%(path)s is already versioned"
 
-    def __init__(self, path, contextInfo=None):
+    def __init__(self, path, context_info=None):
         """Construct a new NotVersionedError.
 
         :param path: This is the path which is versioned, 
         which should be in a user friendly form.
-        :param contextInfo: If given, this is information about the context,
+        :param context_info: If given, this is information about the context,
         which could explain why this is expected to not be versioned.
         """
         BzrError.__init__(self)
         self.path = path
-        if contextInfo is None:
-            self.contextInfo = ''
+        if context_info is None:
+            self.context_info = ''
         else:
-            self.contextInfo = contextInfo + ": "
+            self.context_info = context_info + ". "
 
 
 class NotVersionedError(BzrError):
-    #Used when a path is expected to be versioned, but it is not.
+    """Used when a path is expected to be versioned, but it is not."""
     
-    _fmt = "%(contextInfo)s%(path)s is not versioned"
+    _fmt = "%(context_info)s%(path)s is not versioned"
 
-    def __init__(self, path, contextInfo=None):
+    def __init__(self, path, context_info=None):
         """Construct a new NotVersionedError.
 
         :param path: This is the path which is not versioned, 
         which should be in a user friendly form.
-        :param contextInfo: If given, this is information about the context,
+        :param context_info: If given, this is information about the context,
         which could explain why this is expected to be versioned.
         """
         BzrError.__init__(self)
         self.path = path
-        if contextInfo is None:
-            self.contextInfo = ''
+        if context_info is None:
+            self.context_info = ''
         else:
-            self.contextInfo = contextInfo + ": "
-        
-        
+            self.context_info = context_info + ". "
+
+
 class PathsNotVersionedError(BzrError):
-    # used when reporting several paths which are not versioned
+    """Used when reporting several paths which are not versioned"""
 
     _fmt = "Path(s) are not versioned: %(paths_as_string)s"
 
@@ -1318,6 +1323,48 @@
     _fmt = "Moving the root directory is not supported at this time"
 
 
+class BzrMoveFailedError(BzrError):
+    
+    _fmt = "Could not move %(from_path)s%(operator)s %(to_path)s%(extra)s"
+
+    def __init__(self, from_path, to_path, extra=None):
+        BzrError.__init__(self)
+        if extra:
+            self.extra = ': ' + str(extra)
+        else:
+            self.extra = ''
+            
+        has_from = len(from_path) > 0
+        has_to = len(to_path) > 0
+        if has_from:
+            self.from_path = osutils.splitpath(from_path)[-1]
+        else:
+            self.from_path = ''
+            
+        if has_to:
+            self.to_path = osutils.splitpath(to_path)[-1]
+        else:
+            self.to_path = ''
+
+        self.operator = ""
+        if has_from and has_to:
+            self.operator = " =>"
+        elif has_from:            
+            self.from_path = "from " + from_path
+        elif has_to:
+            self.operator = "to"
+        else:
+            self.operator = "file"
+
+
+class BzrRenameFailedError(BzrMoveFailedError):
+    
+    _fmt = "Could not rename %(from_path)s%(operator)s %(to_path)s%(extra)s"
+    
+    def __init__(self, from_path, to_path, extra=None):
+        BzrMoveFailedError.__init__(self, from_path, to_path, extra)
+
+
 class BzrBadParameterNotString(BzrBadParameter):
 
     _fmt = "Parameter %(param)s is not a string or unicode string."

=== modified file 'bzrlib/tests/blackbox/test_mv.py'
--- bzrlib/tests/blackbox/test_mv.py	2006-12-21 19:14:35 +0000
+++ bzrlib/tests/blackbox/test_mv.py	2006-12-22 18:22:50 +0000
@@ -26,7 +26,9 @@
     TestCaseWithTransport,
     TestSkipped,
     )
-
+from bzrlib.osutils import (
+    splitpath
+    )
 
 class TestMove(TestCaseWithTransport):
 
@@ -61,12 +63,14 @@
     def test_mv_unversioned(self):
         self.build_tree(['unversioned.txt'])
         self.run_bzr_error(
-            ["^bzr: ERROR: can't rename: old name .* is not versioned$"],
+            ["^bzr: ERROR: Could not rename unversioned.txt => elsewhere."
+             " .*unversioned.txt is not versioned$"],
             'mv', 'unversioned.txt', 'elsewhere')
 
     def test_mv_nonexisting(self):
         self.run_bzr_error(
-            ["^bzr: ERROR: can't rename: old name .* is not versioned$"],
+            ["^bzr: ERROR: Could not rename doesnotexist => somewhereelse."
+             " .*doesnotexist is not versioned$"],
             'mv', 'doesnotexist', 'somewhereelse')
 
     def test_mv_unqualified(self):
@@ -78,11 +82,12 @@
         tree.add(['test.txt'])
 
         self.run_bzr_error(
-            ["^bzr: ERROR: Invalid move destination: .* is not versioned$"],
+            ["^bzr: ERROR: Could not move to sub1: sub1 is not versioned$"],
             'mv', 'test.txt', 'sub1')
-        
+
         self.run_bzr_error(
-            ["^bzr: ERROR: destination .* is not a versioned directory$"],
+            ["^bzr: ERROR: Could not move test.txt => .*hello.txt: "
+             "sub1 is not versioned$"],
             'mv', 'test.txt', 'sub1/hello.txt')
         
     def test_mv_dirs(self):
@@ -171,7 +176,7 @@
     
         os.remove('a')
         self.run_bzr_error(
-            ["^bzr: ERROR: Invalid move destination: .* is already versioned$"],
+            ["^bzr: ERROR: Could not move a => b. b is already versioned$"],
             'mv', 'a', 'b')
         self.failIfExists('a')
         self.failUnlessExists('b')
@@ -199,7 +204,7 @@
 
         os.rename('a', 'sub1/a')
         self.run_bzr_error(
-            ["^bzr: ERROR: destination .* is not a versioned directory$"],
+            ["^bzr: ERROR: Could not move a => a: sub1 is not versioned$"],
             'mv', 'a', 'sub1/a')
         self.failIfExists('a')
         self.failUnlessExists('sub1/a')
@@ -228,8 +233,8 @@
         tree.commit('initial commit')
 
         os.rename('a1', 'sub1/a1')
-        self.run_bzr_error(            
-            ["^bzr: ERROR: Invalid move destination: .* is not versioned$"],
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not move to sub1. sub1 is not versioned$"],
             'mv', 'a1', 'a2', 'sub1')
         self.failIfExists('a1')
         self.failUnlessExists('a2')
@@ -245,8 +250,8 @@
         tree.commit('initial commit')
 
         self.run_bzr_error(
-            ["^bzr: ERROR: File\(s\) exist: .+ .+: can't rename."
-             " Use option '--after' to force rename."],
+            ["^bzr: ERROR: Could not rename a => b: Files exist: a b:"
+             " \(Use option '--after' to force rename\)$"],
             'mv', 'a', 'b')
         self.failUnlessExists('a')
         self.failUnlessExists('b')
@@ -273,8 +278,8 @@
         tree.commit('initial commit')
 
         self.run_bzr_error(
-            ["^bzr: ERROR: File\(s\) exist: .+ .+: can't rename."
-             " Use option '--after' to force rename."],
+            ["^bzr: ERROR: Could not rename a1 => a1: Files exist: a1 .*a1:"
+             " \(Use option '--after' to force rename\)$"],
             'mv', 'a1', 'a2', 'sub1')
         self.failUnlessExists('a1')
         self.failUnlessExists('a2')

=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py	2006-12-21 19:14:35 +0000
+++ bzrlib/workingtree.py	2006-12-22 18:22:50 +0000
@@ -72,20 +72,7 @@
 
 from bzrlib import symbol_versioning
 from bzrlib.decorators import needs_read_lock, needs_write_lock
-from bzrlib.errors import (AlreadyVersionedError,
-                           BzrCheckError,
-                           BzrError,
-                           ConflictFormatError,
-                           FilesExist,                     
-                           NotADirectory,
-                           NotBranchError,
-                           NotInWorkingDirectory,
-                           NoSuchFile,
-                           NotVersionedError,
-                           MergeModifiedFormatError,
-                           UnsupportedOperation,
-                           WeaveRevisionNotPresent,
-                           )
+                           
 from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
 from bzrlib.lockable_files import LockableFiles, TransportLock
 from bzrlib.lockdir import LockDir
@@ -95,12 +82,12 @@
     compact_date,
     file_kind,
     isdir,
-    pathjoin,
+    normpath,
+    pathjoin,    
+    rand_chars,    
+    realpath,
     safe_unicode,
     splitpath,
-    rand_chars,
-    normpath,
-    realpath,
     supports_executable,
     )
 from bzrlib.trace import mutter, note
@@ -243,8 +230,8 @@
         mutter("opening working tree %r", basedir)
         if deprecated_passed(branch):
             if not _internal:
-                warnings.warn("WorkingTree(..., branch=XXX) is deprecated as of bzr 0.8."
-                     " Please use bzrdir.open_workingtree() or"
+                warnings.warn("WorkingTree(..., branch=XXX) is deprecated"
+                     " as of bzr 0.8. Please use bzrdir.open_workingtree() or"
                      " WorkingTree.open().",
                      DeprecationWarning,
                      stacklevel=2
@@ -269,7 +256,8 @@
         # if needed, or, when the cache sees a change, append it to the hash
         # cache file, and have the parser take the most recent entry for a
         # given path only.
-        cache_filename = self.bzrdir.get_workingtree_transport(None).local_abspath('stat-cache')
+        cache_filename = self.bzrdir.get_workingtree_transport(None) \
+            .local_abspath('stat-cache')
         self._hashcache = hashcache.HashCache(basedir, cache_filename,
                                               self._control_files._file_mode)
         hc = self._hashcache
@@ -399,7 +387,7 @@
                 if inv is not None and inv.revision_id == revision_id:
                     return bzrlib.revisiontree.RevisionTree(
                         self.branch.repository, inv, revision_id)
-            except (NoSuchFile, errors.BadInventoryFormat):
+            except (errors.NoSuchFile, errors.BadInventoryFormat):
                 pass
         # No cached copy available, retrieve from the repository.
         # FIXME? RBC 20060403 should we cache the inventory locally
@@ -517,7 +505,7 @@
             parents = [last_rev]
         try:
             merges_file = self._control_files.get_utf8('pending-merges')
-        except NoSuchFile:
+        except errors.NoSuchFile:
             pass
         else:
             for l in merges_file.readlines():
@@ -636,7 +624,7 @@
                     kinds[pos] = file_kind(fullpath)
                 except OSError, e:
                     if e.errno == errno.ENOENT:
-                        raise NoSuchFile(fullpath)
+                        raise errors.NoSuchFile(fullpath)
 
     @needs_write_lock
     def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
@@ -651,8 +639,8 @@
         :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
         """
         parents = self.get_parent_ids() + [revision_id]
-        self.set_parent_ids(parents,
-            allow_leftmost_as_ghost=len(parents) > 1 or allow_leftmost_as_ghost)
+        self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1 
+            or allow_leftmost_as_ghost)
 
     @needs_tree_write_lock
     def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
@@ -792,9 +780,9 @@
         """Merge from a branch into this working tree.
 
         :param branch: The branch to merge from.
-        :param to_revision: If non-None, the merge will merge to to_revision, but 
-            not beyond it. to_revision does not need to be in the history of
-            the branch when it is supplied. If None, to_revision defaults to
+        :param to_revision: If non-None, the merge will merge to to_revision, 
+            but not beyond it. to_revision does not need to be in the history 
+            of the branch when it is supplied. If None, to_revision defaults to
             branch.last_revision().
         """
         from bzrlib.merge import Merger, Merge3Merger
@@ -834,14 +822,14 @@
     def merge_modified(self):
         try:
             hashfile = self._control_files.get('merge-hashes')
-        except NoSuchFile:
+        except errors.NoSuchFile:
             return {}
         merge_hashes = {}
         try:
             if hashfile.next() != MERGE_MODIFIED_HEADER_1 + '\n':
-                raise MergeModifiedFormatError()
+                raise errors.MergeModifiedFormatError()
         except StopIteration:
-            raise MergeModifiedFormatError()
+            raise errors.MergeModifiedFormatError()
         for s in RioReader(hashfile):
             file_id = s.get("file_id")
             if file_id not in self.inventory:
@@ -965,9 +953,9 @@
 
                 if f_ie:
                     if f_ie.kind != fk:
-                        raise BzrCheckError("file %r entered as kind %r id %r, "
-                                            "now of kind %r"
-                                            % (fap, f_ie.kind, f_ie.file_id, fk))
+                        raise errors.BzrCheckError(
+                            "file %r entered as kind %r id %r, now of kind %r"
+                            % (fap, f_ie.kind, f_ie.file_id, fk))
 
                 # make a last minute entry
                 if f_ie:
@@ -987,7 +975,8 @@
                 new_children.sort()
                 new_children = collections.deque(new_children)
                 stack.append((f_ie.file_id, fp, fap, new_children))
-                # Break out of inner loop, so that we start outer loop with child
+                # Break out of inner loop, 
+                # so that we start outer loop with child
                 break
             else:
                 # if we finished all children, pop it off the stack
@@ -1047,26 +1036,28 @@
         inv = self.inventory
         to_abs = self.abspath(to_dir)
         if not isdir(to_abs):
-            raise NotADirectory(to_abs, extra="Invalid move destination")
+            raise errors.BzrMoveFailedError('',to_dir, 
+                errors.NotADirectory(to_abs))
         if not self.has_filename(to_dir):
-            raise NotInWorkingDirectory(to_dir, extra=
-                "(Invalid move destination)")
+            raise errors.BzrMoveFailedError('',to_dir,
+                errors.NotInWorkingDirectory(to_dir))
         to_dir_id = inv.path2id(to_dir)
         if to_dir_id is None:
-            raise NotVersionedError(path=str(to_dir),
-                contextInfo="Invalid move destination")
+            raise errors.BzrMoveFailedError('',to_dir,
+                errors.NotVersionedError(path=str(to_dir)))
             
         to_dir_ie = inv[to_dir_id]
         if to_dir_ie.kind != 'directory':
-            raise NotADirectory(to_abs, extra="Invalid move destination")
+            raise errors.BzrMoveFailedError('',to_dir,
+                errors.NotADirectory(to_abs))
 
         # create rename entries and tuples
         for from_rel in from_paths:
             from_tail = splitpath(from_rel)[-1]
             from_id = inv.path2id(from_rel)
             if from_id is None:
-                raise NotVersionedError(path=str(from_rel),
-                    contextInfo="Invalid source")
+                raise errors.BzrMoveFailedError(from_rel,to_dir,
+                    errors.NotVersionedError(path=str(from_rel)))
 
             from_entry = inv[from_id]
             from_parent_id = from_entry.parent_id
@@ -1114,18 +1105,19 @@
 
             # check the inventory for source and destination
             if from_id is None:
-                raise NotVersionedError(path=str(from_rel),
-                    contextInfo="Invalid move source")
+                raise errors.BzrMoveFailedError(from_rel,to_rel,
+                    errors.NotVersionedError(path=str(from_rel)))
             if to_id is not None:
-                raise AlreadyVersionedError(path=str(to_rel),
-                    contextInfo="Invalid move destination")
+                raise errors.BzrMoveFailedError(from_rel,to_rel,
+                    errors.AlreadyVersionedError(path=str(to_rel)))
 
             # try to determine the mode for rename (only change inv or change 
             # inv and file system)
             if after:
                 if not self.has_filename(to_rel):
-                    raise NoSuchFile(path=str(to_rel),
-                        extra="New file has not been created yet")
+                    raise errors.BzrMoveFailedError(from_id,to_rel,
+                        errors.NoSuchFile(path=str(to_rel),
+                        extra="New file has not been created yet"))
                 only_change_inv = True
             elif not self.has_filename(from_rel) and self.has_filename(to_rel):
                 only_change_inv = True
@@ -1135,12 +1127,13 @@
                 # something is wrong, so lets determine what exactly
                 if not self.has_filename(from_rel) and \
                    not self.has_filename(to_rel):
-                    raise PathsDoNotExist(paths=(str(from_rel), str(to_rel)),
-                        extra="can't rename")
+                    raise errors.BzrRenameFailedError(from_rel,to_rel,
+                        errors.PathsDoNotExist(paths=(str(from_rel), 
+                        str(to_rel))))
                 else:
-                    raise FilesExist(paths=(str(from_rel), str(to_rel)),
-                        extra="can't rename. Use option '--after' to"
-                              " force rename.")
+                    raise errors.BzrRenameFailedError(from_rel,to_rel,
+                        errors.FilesExist(paths=(str(from_rel), str(to_rel)),
+                        extra="(Use option '--after' to force rename)"))
             rename_entry.only_change_inv = only_change_inv                       
         return rename_entries
 
@@ -1158,9 +1151,9 @@
                 self._move_entry(entry)
             except OSError, e:
                 self._rollback_move(moved)
-                raise BzrError("failed to rename %r to %r: %s" %
-                               (entry.from_rel, entry.to_rel, e[1]))
-            except BzrError, e:
+                raise errors.BzrRenameFailedError(entry.from_rel, entry.to_rel,
+                    e[1])
+            except errors.BzrError, e:
                 self._rollback_move(moved)
                 raise
             moved.append(entry)
@@ -1174,10 +1167,11 @@
             try:
                 self._move_entry(entry, inverse=True)
             except OSError, e:
-                raise BzrError("error moving files. Rollback failed. The"
-                               " working tree is in an inconsistent state."
-                               " Please consider doing a 'bzr revert'."
-                               " Error message is: %s" % e[1])
+                raise errors.BzrMoveFailedError( '', '', 
+                    errors.BzrError("Rollback failed."
+                        " The working tree is in an inconsistent state."
+                        " Please consider doing a 'bzr revert'."
+                        " Error message is: %s" % e[1]))
 
     def _move_entry(self, entry, inverse=False):
         inv = self.inventory
@@ -1185,8 +1179,8 @@
         to_rel_abs = self.abspath(entry.to_rel)
 
         if from_rel_abs == to_rel_abs:
-            raise BzrError("error moving files. Source %r and target %r are"
-                           " identical." % (from_rel, to_rel))
+            raise errors.BzrMoveFailedError(from_rel, to_rel,
+                "Source and target are identical.")
                            
         if inverse:
             if not entry.only_change_inv:
@@ -1228,8 +1222,8 @@
         from_tail = splitpath(from_rel)[-1]
         from_id = inv.path2id(from_rel)
         if from_id is None:
-            raise BzrError("can't rename: old name %r is not versioned" % 
-                           from_rel)
+            raise errors.BzrRenameFailedError(from_rel,to_rel,
+                errors.NotVersionedError(path=str(from_rel)))
         from_entry = inv[from_id]
         from_parent_id = from_entry.parent_id
         to_dir, to_tail = os.path.split(to_rel)
@@ -1248,8 +1242,8 @@
         # check if the target changed directory and if the target directory is
         # versioned
         if to_dir_id is None:
-            raise BzrError("destination %r is not a versioned"
-                           " directory" % to_dir)
+            raise errors.BzrMoveFailedError(from_rel,to_rel,
+                errors.NotVersionedError(path=str(to_dir)))
         
         # all checks done. now we can continue with our actual work
         mutter('rename_one:\n'
@@ -1658,7 +1652,7 @@
             if not fid:
                 # TODO: Perhaps make this just a warning, and continue?
                 # This tends to happen when 
-                raise NotVersionedError(path=f)
+                raise errors.NotVersionedError(path=f)
             if verbose:
                 # having remove it, it must be either ignored or unknown
                 if self.is_ignored(f):
@@ -1707,7 +1701,7 @@
             elif kind == 'symlink':
                 inv.add(InventoryLink(file_id, name, parent))
             else:
-                raise BzrError("unknown kind %r" % kind)
+                raise errors.BzrError("unknown kind %r" % kind)
         self._write_inventory(inv)
 
     @needs_tree_write_lock
@@ -1888,10 +1882,10 @@
         self.flush()
 
     def set_conflicts(self, arg):
-        raise UnsupportedOperation(self.set_conflicts, self)
+        raise errors.UnsupportedOperation(self.set_conflicts, self)
 
     def add_conflicts(self, arg):
-        raise UnsupportedOperation(self.add_conflicts, self)
+        raise errors.UnsupportedOperation(self.add_conflicts, self)
 
     @needs_read_lock
     def conflicts(self):
@@ -1971,7 +1965,7 @@
         """See Mutable.last_revision."""
         try:
             return self._control_files.get_utf8('last-revision').read()
-        except NoSuchFile:
+        except errors.NoSuchFile:
             return None
 
     def _change_last_revision(self, revision_id):
@@ -2002,13 +1996,13 @@
     def conflicts(self):
         try:
             confile = self._control_files.get('conflicts')
-        except NoSuchFile:
+        except errors.NoSuchFile:
             return _mod_conflicts.ConflictList()
         try:
             if confile.next() != CONFLICT_HEADER_1 + '\n':
-                raise ConflictFormatError()
+                raise errors.ConflictFormatError()
         except StopIteration:
-            raise ConflictFormatError()
+            raise errors.ConflictFormatError()
         return _mod_conflicts.ConflictList.from_stanzas(RioReader(confile))
 
     def unlock(self):
@@ -2076,7 +2070,7 @@
             transport = a_bzrdir.get_workingtree_transport(None)
             format_string = transport.get("format").read()
             return klass._formats[format_string]
-        except NoSuchFile:
+        except errors.NoSuchFile:
             raise errors.NoWorkingTree(base=transport.base)
         except KeyError:
             raise errors.UnknownFormatError(format=format_string)


More information about the bazaar mailing list