[MERGE] enhanced mv command

Marius Kruger amanic at gmail.com
Fri Jan 19 19:52:33 GMT 2007


strike 5

attached diff and zipped bundle again,
hope its more better this time.

On 1/18/07, Aaron Bentley <aaron.bentley at utoronto.ca> wrote:

> > Would you like me to address the comments lying around the list and
> > submit another patch.
>
> That's not necessary to me, since I've promised to address them when
> merging.  I don't think you need to, but it might be a bit clearer.
>

here is the stuff I addressed:

1)  Changed "FilesExist" to more specific "RenameFailedFilesExist"  (John's
comments)
     - I didn't realize that its introduced and thus only used by this
patch.
       I did though it might be useful to have a generic exception,
       but FilesExist isn't something which would often be a problem.
2) Fixed errors.py import order
3) Improved WorkingTree._move Exception catching as best I could
4) Removed extra blank lines from doc strings in test_mv.py
    as suggested by martin. I thought it is neccessary after reading pep257
5) Removed assertNone,
    from using other unittesting frameworks (c++& java),
    I'm sort of used to have a asser*Null, but if you guys really don't
think its
    nice, what can I say.
    I still prefer
        self.assertNone(tree.path2id(path))
        self.assertNone(tree.path2id(path), path+' not in working tree.')
    over
        self.assertIsNot(tree.path2id(path), None)
        self.assertIsNot(tree.path2id(path), None, path+' not in working
tree.')

-- 



I code therefore I am.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.ubuntu.com/archives/bazaar/attachments/20070119/e33e9f1d/attachment-0001.htm 
-------------- next part --------------
=== modified file 'NEWS'
--- NEWS	2007-01-18 03:58:02 +0000
+++ NEWS	2007-01-18 04:11:33 +0000
@@ -1,7 +1,17 @@
 IN DEVELOPMENT
-
   IMPROVEMENTS:
 
+    * ``bzr mv`` enhanced to support already moved files.
+      In the past the mv command would have failed if the source file doesn't
+      exist. In this situation ``bzr mv`` would now detect that the file has
+      already moved and update the repository accordingly, if the target file
+      does exist.
+      A new option ``--after`` has been added so that if two files already
+      exist, you could notify Bazaar that you have moved a (versioned) file and
+      replaced it with another. Thus in this case ``bzr move --after`` will
+      only update the Bazaar identifier.
+      (Steffen Eichenberg, Marius Kruger)
+
     * ``ls`` now works on treeless branches and remote branches.
       (Aaron Bentley)
 

=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py	2007-01-18 03:58:02 +0000
+++ bzrlib/builtins.py	2007-01-18 04:05:43 +0000
@@ -450,16 +450,25 @@
 
     If the last argument is a versioned directory, all the other names
     are moved into it.  Otherwise, there must be exactly two arguments
-    and the file is changed to a new name, which must not already exist.
+    and the file is changed to a new name.
+
+    If OLDNAME does not exist on the filesystem but is versioned and
+    NEWNAME does exist on the filesystem but is not versioned, mv
+    assumes that the file has been manually moved and only updates
+    its internal inventory to reflect that change.
+    The same is valid when moving many SOURCE files to a DESTINATION.
 
     Files cannot be moved between branches.
     """
 
     takes_args = ['names*']
+    takes_options = [Option("after", help="move only the bzr identifier"
+        " of the file (file has already been moved). Use this flag if"
+        " bzr is not able to detect this itself.")]
     aliases = ['move', 'rename']
     encoding_type = 'replace'
 
-    def run(self, names_list):
+    def run(self, names_list, after=False):
         if names_list is None:
             names_list = []
 
@@ -469,13 +478,14 @@
         
         if os.path.isdir(names_list[-1]):
             # move into existing directory
-            for pair in tree.move(rel_names[:-1], rel_names[-1]):
+            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
                 self.outf.write("%s => %s\n" % pair)
         else:
             if len(names_list) != 2:
-                raise errors.BzrCommandError('to mv multiple files the destination '
-                                             'must be a versioned directory')
-            tree.rename_one(rel_names[0], rel_names[1])
+                raise errors.BzrCommandError('to mv multiple files the'
+                                             ' destination must be a versioned'
+                                             ' directory')
+            tree.rename_one(rel_names[0], rel_names[1], after=after)
             self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
             
     

=== modified file 'bzrlib/errors.py'
--- bzrlib/errors.py	2007-01-18 03:58:02 +0000
+++ bzrlib/errors.py	2007-01-19 19:23:35 +0000
@@ -18,12 +18,17 @@
 """
 
 
-from bzrlib import symbol_versioning
-from bzrlib.patches import (PatchSyntax, 
-                            PatchConflict, 
-                            MalformedPatchHeader,
-                            MalformedHunkHeader,
-                            MalformedLine,)
+from bzrlib import (
+    osutils,
+    symbol_versioning,
+    )
+from bzrlib.patches import (
+    MalformedHunkHeader,
+    MalformedLine,
+    MalformedPatchHeader,
+    PatchConflict,
+    PatchSyntax,
+    )
 
 
 # TODO: is there any value in providing the .args field used by standard
@@ -333,6 +338,32 @@
     _fmt = "File exists: %(path)r%(extra)s"
 
 
+class RenameFailedFilesExist(BzrError):
+    """Used when renaming and both source and dest exist."""
+
+    _fmt = ("Could not rename %(source)s => %(dest)s because both files exist."
+         "%(extra)s")
+
+    def __init__(self, source, dest, extra=None):
+        BzrError.__init__(self)
+        self.source = str(source)
+        self.dest = str(dest)
+        if extra:
+            self.extra = ' ' + str(extra)
+        else:
+            self.extra = ''
+
+
+class NotADirectory(PathError):
+
+    _fmt = "%(path)r is not a directory %(extra)s"
+
+
+class NotInWorkingDirectory(PathError):
+
+    _fmt = "%(path)r is not in the working directory %(extra)s"
+
+
 class DirectoryNotEmpty(PathError):
 
     _fmt = "Directory not empty: %(path)r%(extra)s"
@@ -505,17 +536,50 @@
         self.repo_format = repo_format
 
 
+class AlreadyVersionedError(BzrError):
+    """Used when a path is expected not to be versioned, but it is."""
+
+    _fmt = "%(context_info)s%(path)s is already versioned"
+
+    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 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 context_info is None:
+            self.context_info = ''
+        else:
+            self.context_info = context_info + ". "
+
+
 class NotVersionedError(BzrError):
-
-    _fmt = "%(path)s is not versioned"
-
-    def __init__(self, path):
+    """Used when a path is expected to be versioned, but it is not."""
+
+    _fmt = "%(context_info)s%(path)s is not versioned"
+
+    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 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 context_info is None:
+            self.context_info = ''
+        else:
+            self.context_info = context_info + ". "
 
 
 class PathsNotVersionedError(BzrError):
-    # used when reporting several paths are not versioned
+    """Used when reporting several paths which are not versioned"""
 
     _fmt = "Path(s) are not versioned: %(paths_as_string)s"
 
@@ -528,17 +592,21 @@
 
 class PathsDoNotExist(BzrError):
 
-    _fmt = "Path(s) do not exist: %(paths_as_string)s"
+    _fmt = "Path(s) do not exist: %(paths_as_string)s%(extra)s"
 
     # used when reporting that paths are neither versioned nor in the working
     # tree
 
-    def __init__(self, paths):
+    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])
+        if extra:
+            self.extra = ': ' + str(extra)
+        else:
+            self.extra = ''
 
 
 class BadFileKindError(BzrError):
@@ -1292,6 +1360,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/__init__.py'
--- bzrlib/tests/__init__.py	2007-01-17 04:32:59 +0000
+++ bzrlib/tests/__init__.py	2007-01-19 19:40:18 +0000
@@ -661,9 +661,19 @@
                 excName = str(excClass)
             raise self.failureException, "%s not raised" % excName
 
-    def assertIs(self, left, right):
+    def assertIs(self, left, right, message=None):
         if not (left is right):
-            raise AssertionError("%r is not %r." % (left, right))
+            if message is not None:
+                raise AssertionError(message)
+            else:
+                raise AssertionError("%r is not %r." % (left, right))
+
+    def assertIsNot(self, left, right, message=None):
+        if (left is right):
+            if message is not None:
+                raise AssertionError(message)
+            else:
+                raise AssertionError("%r is %r." % (left, right))
 
     def assertTransportMode(self, transport, path, mode):
         """Fail if a path does not have mode mode.
@@ -1539,11 +1549,11 @@
 
     def failUnlessExists(self, path):
         """Fail unless path, which may be abs or relative, exists."""
-        self.failUnless(osutils.lexists(path))
+        self.failUnless(osutils.lexists(path),path+" does not exist")
 
     def failIfExists(self, path):
         """Fail if path, which may be abs or relative, exists."""
-        self.failIf(osutils.lexists(path))
+        self.failIf(osutils.lexists(path),path+" exists")
 
 
 class TestCaseWithTransport(TestCaseInTempDir):

=== modified file 'bzrlib/tests/blackbox/test_mv.py'
--- bzrlib/tests/blackbox/test_mv.py	2006-12-18 12:10:57 +0000
+++ bzrlib/tests/blackbox/test_mv.py	2007-01-19 19:39:39 +0000
@@ -26,10 +26,29 @@
     TestCaseWithTransport,
     TestSkipped,
     )
-
+from bzrlib.osutils import (
+    splitpath
+    )
 
 class TestMove(TestCaseWithTransport):
 
+    def assertInWorkingTree(self,path):
+        tree = workingtree.WorkingTree.open('.')
+        self.assertIsNot(tree.path2id(path), None,
+            path+' not in working tree.')
+
+    def assertNotInWorkingTree(self,path):
+        tree = workingtree.WorkingTree.open('.')
+        self.assertIs(tree.path2id(path), None, path+' in working tree.')
+
+    def assertMoved(self,from_path,to_path):
+        """Assert that to_path is existing and versioned but from_path not. """
+        self.failIfExists(from_path)
+        self.assertNotInWorkingTree(from_path)
+
+        self.failUnlessExists(to_path)
+        self.assertInWorkingTree(to_path)
+
     def test_mv_modes(self):
         """Test two modes of operation for mv"""
         tree = self.make_branch_and_tree('.')
@@ -37,94 +56,86 @@
         tree.add(['a', 'c', 'subdir'])
 
         self.run_bzr('mv', 'a', 'b')
-        self.failUnlessExists('b')
-        self.failIfExists('a')
+        self.assertMoved('a','b')
 
         self.run_bzr('mv', 'b', 'subdir')
-        self.failUnlessExists('subdir/b')
-        self.failIfExists('b')
+        self.assertMoved('b','subdir/b')
 
         self.run_bzr('mv', 'subdir/b', 'a')
-        self.failUnlessExists('a')
-        self.failIfExists('subdir/b')
+        self.assertMoved('subdir/b','a')
 
         self.run_bzr('mv', 'a', 'c', 'subdir')
-        self.failUnlessExists('subdir/a')
-        self.failUnlessExists('subdir/c')
-        self.failIfExists('a')
-        self.failIfExists('c')
+        self.assertMoved('a','subdir/a')
+        self.assertMoved('c','subdir/c')
 
         self.run_bzr('mv', 'subdir/a', 'subdir/newa')
-        self.failUnlessExists('subdir/newa')
-        self.failIfExists('subdir/a')
+        self.assertMoved('subdir/a','subdir/newa')
 
     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 working file .* does not exist$"],
+            ["^bzr: ERROR: Could not rename doesnotexist => somewhereelse."
+             " .*doesnotexist is not versioned$"],
             'mv', 'doesnotexist', 'somewhereelse')
 
     def test_mv_unqualified(self):
         self.run_bzr_error(['^bzr: ERROR: missing file argument$'], 'mv')
-        
+
     def test_mv_invalid(self):
         tree = self.make_branch_and_tree('.')
         self.build_tree(['test.txt', 'sub1/'])
         tree.add(['test.txt'])
 
         self.run_bzr_error(
-            ["^bzr: ERROR: destination u'sub1' is not a versioned directory$"],
+            ["^bzr: ERROR: Could not move to sub1: sub1 is not versioned$"],
             'mv', 'test.txt', 'sub1')
-        
+
         self.run_bzr_error(
-            ["^bzr: ERROR: can't determine destination directory id for u'sub1'$"],
+            ["^bzr: ERROR: Could not move test.txt => .*hello.txt: "
+             "sub1 is not versioned$"],
             'mv', 'test.txt', 'sub1/hello.txt')
-        
+
     def test_mv_dirs(self):
         tree = self.make_branch_and_tree('.')
         self.build_tree(['hello.txt', 'sub1/'])
         tree.add(['hello.txt', 'sub1'])
 
         self.run_bzr('mv', 'sub1', 'sub2')
-        self.failUnlessExists('sub2')
-        self.failIfExists('sub1')
+        self.assertMoved('sub1','sub2')
+
         self.run_bzr('mv', 'hello.txt', 'sub2')
-        self.failUnlessExists("sub2/hello.txt")
-        self.failIfExists("hello.txt")
+        self.assertMoved('hello.txt','sub2/hello.txt')
 
         tree.read_working_inventory()
-        tree.commit('commit with some things moved to subdirs')
 
         self.build_tree(['sub1/'])
         tree.add(['sub1'])
         self.run_bzr('mv', 'sub2/hello.txt', 'sub1')
-        self.failIfExists('sub2/hello.txt')
-        self.failUnlessExists('sub1/hello.txt')
+        self.assertMoved('sub2/hello.txt','sub1/hello.txt')
+
         self.run_bzr('mv', 'sub2', 'sub1')
-        self.failIfExists('sub2')
-        self.failUnlessExists('sub1/sub2')
+        self.assertMoved('sub2','sub1/sub2')
 
     def test_mv_relative(self):
         self.build_tree(['sub1/', 'sub1/sub2/', 'sub1/hello.txt'])
         tree = self.make_branch_and_tree('.')
         tree.add(['sub1', 'sub1/sub2', 'sub1/hello.txt'])
-        tree.commit('initial tree')
 
         os.chdir('sub1/sub2')
         self.run_bzr('mv', '../hello.txt', '.')
         self.failUnlessExists('./hello.txt')
         tree.read_working_inventory()
-        tree.commit('move to parent directory')
 
         os.chdir('..')
-
         self.run_bzr('mv', 'sub2/hello.txt', '.')
-        self.failUnlessExists('hello.txt')
+        os.chdir('..')
+        self.assertMoved('sub1/sub2/hello.txt','sub1/hello.txt')
 
     def test_mv_smoke_aliases(self):
         # just test that aliases for mv exist, if their behaviour is changed in
@@ -147,3 +158,215 @@
         self.run_bzr('mv', 'c/b', 'b')
         tree = workingtree.WorkingTree.open('.')
         self.assertEqual('b-id', tree.path2id('b'))
+
+    def test_mv_already_moved_file(self):
+        """Test bzr mv original_file to moved_file.
+
+        Tests if a file which has allready been moved by an external tool,
+        is handled correctly by bzr mv.
+        Setup: a is in the working tree, b does not exist.
+        User does: mv a b; bzr mv a b
+        """
+        self.build_tree(['a'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a'])
+
+        os.rename('a', 'b')
+        self.run_bzr('mv', 'a', 'b')
+        self.assertMoved('a','b')
+
+    def test_mv_already_moved_file_to_versioned_target(self):
+        """Test bzr mv existing_file to versioned_file.
+
+        Tests if an attempt to move an existing versioned file
+        to another versiond file will fail.
+        Setup: a and b are in the working tree.
+        User does: rm b; mv a b; bzr mv a b
+        """
+        self.build_tree(['a', 'b'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a', 'b'])
+
+        os.remove('b')
+        os.rename('a', 'b')
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not move a => b. b is already versioned$"],
+            'mv', 'a', 'b')
+        #check that nothing changed
+        self.failIfExists('a')
+        self.failUnlessExists('b')
+
+    def test_mv_already_moved_file_into_subdir(self):
+        """Test bzr mv original_file to versioned_directory/file.
+
+        Tests if a file which has already been moved into a versioned
+        directory by an external tool, is handled correctly by bzr mv.
+        Setup: a and sub/ are in the working tree.
+        User does: mv a sub/a; bzr mv a sub/a
+        """
+        self.build_tree(['a', 'sub/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a', 'sub'])
+
+        os.rename('a', 'sub/a')
+        self.run_bzr('mv', 'a', 'sub/a')
+        self.assertMoved('a','sub/a')
+
+    def test_mv_already_moved_file_into_unversioned_subdir(self):
+        """Test bzr mv original_file to unversioned_directory/file.
+
+        Tests if an attempt to move an existing versioned file
+        into an unversioned directory will fail.
+        Setup: a is in the working tree, sub/ is not.
+        User does: mv a sub/a; bzr mv a sub/a
+        """
+        self.build_tree(['a', 'sub/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a'])
+
+        os.rename('a', 'sub/a')
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not move a => a: sub is not versioned$"],
+            'mv', 'a', 'sub/a')
+        self.failIfExists('a')
+        self.failUnlessExists('sub/a')
+
+    def test_mv_already_moved_files_into_subdir(self):
+        """Test bzr mv original_files to versioned_directory.
+
+        Tests if files which has already been moved into a versioned
+        directory by an external tool, is handled correctly by bzr mv.
+        Setup: a1, a2, sub are in the working tree.
+        User does: mv a1 sub/.; bzr mv a1 a2 sub
+        """
+        self.build_tree(['a1', 'a2', 'sub/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'a2', 'sub'])
+
+        os.rename('a1', 'sub/a1')
+        self.run_bzr('mv', 'a1', 'a2', 'sub')
+        self.assertMoved('a1','sub/a1')
+        self.assertMoved('a2','sub/a2')
+
+    def test_mv_already_moved_files_into_unversioned_subdir(self):
+        """Test bzr mv original_file to unversioned_directory.
+
+        Tests if an attempt to move existing versioned file
+        into an unversioned directory will fail.
+        Setup: a1, a2 are in the working tree, sub is not.
+        User does: mv a1 sub/.; bzr mv a1 a2 sub
+        """
+        self.build_tree(['a1', 'a2', 'sub/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'a2'])
+
+        os.rename('a1', 'sub/a1')
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not move to sub. sub is not versioned$"],
+            'mv', 'a1', 'a2', 'sub')
+        self.failIfExists('a1')
+        self.failUnlessExists('sub/a1')
+        self.failUnlessExists('a2')
+        self.failIfExists('sub/a2')
+
+    def test_mv_already_moved_file_forcing_after(self):
+        """Test bzr mv versioned_file to unversioned_file.
+
+        Tests if an attempt to move an existing versioned file to an existing
+        unversioned file will fail, informing the user to use the --after
+        option to force this.
+        Setup: a is in the working tree, b not versioned.
+        User does: mv a b; touch a; bzr mv a b
+        """
+        self.build_tree(['a', 'b'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a'])
+
+        os.rename('a', 'b')
+        self.build_tree(['a']) #touch a
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not rename a => b because both files exist."
+             " \(Use --after to update the Bazaar id\)$"],
+            'mv', 'a', 'b')
+        self.failUnlessExists('a')
+        self.failUnlessExists('b')
+
+    def test_mv_already_moved_file_using_after(self):
+        """Test bzr mv --after versioned_file to unversioned_file.
+
+        Tests if an existing versioned file can be forced to move to an
+        existing unversioned file using the --after option. With the result
+        that bazaar considers the unversioned_file to be moved from
+        versioned_file and versioned_file will become unversioned.
+        Setup: a is in the working tree and b exists.
+        User does: mv a b; touch a; bzr mv a b --after
+        Resulting in a => b and a is unknown.
+        """
+        self.build_tree(['a', 'b'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a'])
+        os.rename('a', 'b')
+        self.build_tree(['a']) #touch a
+
+        self.run_bzr('mv', 'a', 'b', '--after')
+        self.failUnlessExists('a')
+        self.assertNotInWorkingTree('a')#a should be unknown now.
+        self.failUnlessExists('b')
+        self.assertInWorkingTree('b')
+
+    def test_mv_already_moved_files_forcing_after(self):
+        """Test bzr mv versioned_files to directory/unversioned_file.
+
+        Tests if an attempt to move an existing versioned file to an existing
+        unversioned file in some other directory will fail, informing the user
+        to use the --after option to force this.
+
+        Setup: a1, a2, sub are versioned and in the working tree,
+               sub/a1, sub/a2 are in working tree.
+        User does: mv a* sub; touch a1; touch a2; bzr mv a1 a2 sub
+        """
+        self.build_tree(['a1', 'a2', 'sub/', 'sub/a1', 'sub/a2'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'a2', 'sub'])
+        os.rename('a1', 'sub/a1')
+        os.rename('a2', 'sub/a2')
+        self.build_tree(['a1']) #touch a1
+        self.build_tree(['a2']) #touch a2
+
+        self.run_bzr_error(
+            ["^bzr: ERROR: Could not rename a1 => sub/a1 because both files exist."
+             " \(Use --after to update the Bazaar id\)$"],
+            'mv', 'a1', 'a2', 'sub')
+        self.failUnlessExists('a1')
+        self.failUnlessExists('a2')
+        self.failUnlessExists('sub/a1')
+        self.failUnlessExists('sub/a2')
+
+    def test_mv_already_moved_files_using_after(self):
+        """Test bzr mv --after versioned_file to directory/unversioned_file.
+
+        Tests if an existing versioned file can be forced to move to an
+        existing unversioned file in some other directory using the --after
+        option. With the result that bazaar considers
+        directory/unversioned_file to be moved from versioned_file and
+        versioned_file will become unversioned.
+
+        Setup: a1, a2, sub are versioned and in the working tree,
+               sub/a1, sub/a2 are in working tree.
+        User does: mv a* sub; touch a1; touch a2; bzr mv a1 a2 sub --after
+        """
+        self.build_tree(['a1', 'a2', 'sub/', 'sub/a1', 'sub/a2'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'a2', 'sub'])
+        os.rename('a1', 'sub/a1')
+        os.rename('a2', 'sub/a2')
+        self.build_tree(['a1']) #touch a1
+        self.build_tree(['a2']) #touch a2
+
+        self.run_bzr('mv', 'a1', 'a2', 'sub', '--after')
+        self.failUnlessExists('a1')
+        self.failUnlessExists('a2')
+        self.failUnlessExists('sub/a1')
+        self.failUnlessExists('sub/a2')
+        self.assertInWorkingTree('sub/a1')
+        self.assertInWorkingTree('sub/a2')
\ No newline at end of file

=== modified file 'bzrlib/tests/workingtree_implementations/test_workingtree.py'
--- bzrlib/tests/workingtree_implementations/test_workingtree.py	2007-01-18 03:58:02 +0000
+++ bzrlib/tests/workingtree_implementations/test_workingtree.py	2007-01-18 04:05:43 +0000
@@ -682,3 +682,50 @@
                 tree.add, [u'a\u030a'])
         finally:
             osutils.normalized_filename = orig
+
+    def test_move_deprecated_correct_call_named(self):
+        """tree.move has the deprecated parameter 'to_name'.
+        It has been replaced by 'to_dir' for consistency.
+        Test the new API using named parameter"""
+        self.build_tree(['a1', 'sub1/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'sub1'])
+        tree.commit('initial commit')
+        tree.move(['a1'], to_dir='sub1', after=False)
+
+    def test_move_deprecated_correct_call_unnamed(self):
+        """tree.move has the deprecated parameter 'to_name'.
+        It has been replaced by 'to_dir' for consistency.
+        Test the new API using unnamed parameter"""
+        self.build_tree(['a1', 'sub1/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'sub1'])
+        tree.commit('initial commit')
+        tree.move(['a1'], 'sub1', after=False)
+
+    def test_move_deprecated_wrong_call(self):
+        """tree.move has the deprecated parameter 'to_name'.
+        It has been replaced by 'to_dir' for consistency.
+        Test the new API using wrong parameter"""
+        self.build_tree(['a1', 'sub1/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'sub1'])
+        tree.commit('initial commit')
+        self.assertRaises(TypeError, tree.move, ['a1'],
+                          to_this_parameter_does_not_exist='sub1',
+                          after=False)
+
+    def test_move_deprecated_deprecated_call(self):
+        """tree.move has the deprecated parameter 'to_name'.
+        It has been replaced by 'to_dir' for consistency.
+        Test the new API using deprecated parameter"""
+        self.build_tree(['a1', 'sub1/'])
+        tree = self.make_branch_and_tree('.')
+        tree.add(['a1', 'sub1'])
+        tree.commit('initial commit')
+
+        #tree.move(['a1'], to_name='sub1', after=False)
+        self.callDeprecated(['The parameter to_name was deprecated'
+                             ' in version 0.13. Use to_dir instead'],
+                            tree.move, ['a1'], to_name='sub1',
+                            after=False)

=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py	2006-12-20 11:53:30 +0000
+++ bzrlib/workingtree.py	2007-01-19 19:21:47 +0000
@@ -72,16 +72,6 @@
 
 from bzrlib import symbol_versioning
 from bzrlib.decorators import needs_read_lock, needs_write_lock
-from bzrlib.errors import (BzrCheckError,
-                           BzrError,
-                           ConflictFormatError,
-                           WeaveRevisionNotPresent,
-                           NotBranchError,
-                           NoSuchFile,
-                           NotVersionedError,
-                           MergeModifiedFormatError,
-                           UnsupportedOperation,
-                           )
 from bzrlib.inventory import InventoryEntry, Inventory, ROOT_ID
 from bzrlib.lockable_files import LockableFiles, TransportLock
 from bzrlib.lockdir import LockDir
@@ -91,12 +81,12 @@
     compact_date,
     file_kind,
     isdir,
+    normpath,
     pathjoin,
+    rand_chars,
+    realpath,
     safe_unicode,
     splitpath,
-    rand_chars,
-    normpath,
-    realpath,
     supports_executable,
     )
 from bzrlib.trace import mutter, note
@@ -239,8 +229,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
@@ -265,7 +255,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')
+        wt_trans = self.bzrdir.get_workingtree_transport(None)
+        cache_filename = wt_trans.local_abspath('stat-cache')
         self._hashcache = hashcache.HashCache(basedir, cache_filename,
                                               self._control_files._file_mode)
         hc = self._hashcache
@@ -395,7 +386,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
@@ -513,7 +504,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():
@@ -632,7 +623,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):
@@ -647,8 +638,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):
@@ -788,9 +779,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
@@ -830,14 +821,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:
@@ -961,9 +952,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:
@@ -983,130 +974,292 @@
                 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
                 stack.pop()
 
     @needs_tree_write_lock
-    def move(self, from_paths, to_name):
+    def move(self, from_paths, to_dir=None, after=False, **kwargs):
         """Rename files.
 
-        to_name must exist in the inventory.
+        to_dir must exist in the inventory.
 
-        If to_name exists and is a directory, the files are moved into
+        If to_dir exists and is a directory, the files are moved into
         it, keeping their old names.  
 
-        Note that to_name is only the last component of the new name;
+        Note that to_dir is only the last component of the new name;
         this doesn't change the directory.
 
+        For each entry in from_paths the move mode will be determined
+        independently.
+
+        The first mode moves the file in the filesystem and updates the
+        inventory. The second mode only updates the inventory without
+        touching the file on the filesystem. This is the new mode introduced
+        in version 0.15.
+
+        move uses the second mode if 'after == True' and the target is not
+        versioned but present in the working tree.
+
+        move uses the second mode if 'after == False' and the source is
+        versioned but no longer in the working tree, and the target is not
+        versioned but present in the working tree.
+
+        move uses the first mode if 'after == False' and the source is
+        versioned and present in the working tree, and the target is not
+        versioned and not present in the working tree.
+
+        Everything else results in an error.
+
         This returns a list of (from_path, to_path) pairs for each
         entry that is moved.
         """
-        result = []
-        ## TODO: Option to move IDs only
+        rename_entries = []
+        rename_tuples = []
+
+        # check for deprecated use of signature
+        if to_dir is None:
+            to_dir = kwargs.get('to_name', None)
+            if to_dir is None:
+                raise TypeError('You must supply a target directory')
+            else:
+                symbol_versioning.warn('The parameter to_name was deprecated'
+                                       ' in version 0.13. Use to_dir instead',
+                                       DeprecationWarning)
+
+        # check destination directory
         assert not isinstance(from_paths, basestring)
         inv = self.inventory
-        to_abs = self.abspath(to_name)
+        to_abs = self.abspath(to_dir)
         if not isdir(to_abs):
-            raise BzrError("destination %r is not a directory" % to_abs)
-        if not self.has_filename(to_name):
-            raise BzrError("destination %r not in working directory" % to_abs)
-        to_dir_id = inv.path2id(to_name)
-        if to_dir_id is None and to_name != '':
-            raise BzrError("destination %r is not a versioned directory" % to_name)
+            raise errors.BzrMoveFailedError('',to_dir,
+                errors.NotADirectory(to_abs))
+        if not self.has_filename(to_dir):
+            raise errors.BzrMoveFailedError('',to_dir,
+                errors.NotInWorkingDirectory(to_dir))
+        to_dir_id = inv.path2id(to_dir)
+        if to_dir_id is None:
+            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 BzrError("destination %r is not a directory" % to_abs)
-
-        to_idpath = inv.get_idpath(to_dir_id)
-
-        for f in from_paths:
-            if not self.has_filename(f):
-                raise BzrError("%r does not exist in working tree" % f)
-            f_id = inv.path2id(f)
-            if f_id is None:
-                raise BzrError("%r is not versioned" % f)
-            name_tail = splitpath(f)[-1]
-            dest_path = pathjoin(to_name, name_tail)
-            if self.has_filename(dest_path):
-                raise BzrError("destination %r already exists" % dest_path)
-            if f_id in to_idpath:
-                raise BzrError("can't move %r to a subdirectory of itself" % f)
-
-        # OK, so there's a race here, it's possible that someone will
-        # create a file in this interval and then the rename might be
-        # left half-done.  But we should have caught most problems.
-        orig_inv = deepcopy(self.inventory)
+            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 errors.BzrMoveFailedError(from_rel,to_dir,
+                    errors.NotVersionedError(path=str(from_rel)))
+
+            from_entry = inv[from_id]
+            from_parent_id = from_entry.parent_id
+            to_rel = pathjoin(to_dir, from_tail)
+            rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
+                                         from_id=from_id,
+                                         from_tail=from_tail,
+                                         from_parent_id=from_parent_id,
+                                         to_rel=to_rel, to_tail=from_tail,
+                                         to_parent_id=to_dir_id)
+            rename_entries.append(rename_entry)
+            rename_tuples.append((from_rel, to_rel))
+
+        # determine which move mode to use. checks also for movability
+        rename_entries = self._determine_mv_mode(rename_entries, after)
+
         original_modified = self._inventory_is_modified
         try:
             if len(from_paths):
                 self._inventory_is_modified = True
-            for f in from_paths:
-                name_tail = splitpath(f)[-1]
-                dest_path = pathjoin(to_name, name_tail)
-                result.append((f, dest_path))
-                inv.rename(inv.path2id(f), to_dir_id, name_tail)
-                try:
-                    osutils.rename(self.abspath(f), self.abspath(dest_path))
-                except OSError, e:
-                    raise BzrError("failed to rename %r to %r: %s" %
-                                   (f, dest_path, e[1]))
+            self._move(rename_entries)
         except:
             # restore the inventory on error
-            self._set_inventory(orig_inv, dirty=original_modified)
+            self._inventory_is_modified = original_modified
             raise
         self._write_inventory(inv)
-        return result
+        return rename_tuples
+
+    def _determine_mv_mode(self, rename_entries, after=False):
+        """Determines for each from-to pair if both inventory and working tree
+        or only the inventory has to be changed.
+
+        Also does basic plausability tests.
+        """
+        inv = self.inventory
+
+        for rename_entry in rename_entries:
+            # store to local variables for easier reference
+            from_rel = rename_entry.from_rel
+            from_id = rename_entry.from_id
+            to_rel = rename_entry.to_rel
+            to_id = inv.path2id(to_rel)
+            only_change_inv = False
+
+            # check the inventory for source and destination
+            if from_id is None:
+                raise errors.BzrMoveFailedError(from_rel,to_rel,
+                    errors.NotVersionedError(path=str(from_rel)))
+            if to_id is not None:
+                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 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
+            elif self.has_filename(from_rel) and not self.has_filename(to_rel):
+                only_change_inv = False
+            else:
+                # something is wrong, so lets determine what exactly
+                if not self.has_filename(from_rel) and \
+                   not self.has_filename(to_rel):
+                    raise errors.BzrRenameFailedError(from_rel,to_rel,
+                        errors.PathsDoNotExist(paths=(str(from_rel),
+                        str(to_rel))))
+                else:
+                    raise errors.RenameFailedFilesExist(from_rel, to_rel,
+                        extra="(Use --after to update the Bazaar id)")
+            rename_entry.only_change_inv = only_change_inv
+        return rename_entries
+
+    def _move(self, rename_entries):
+        """Moves a list of files.
+
+        Depending on the value of the flag 'only_change_inv', the
+        file will be moved on the file system or not.
+        """
+        inv = self.inventory
+        moved = []
+
+        for entry in rename_entries:
+            try:
+                self._move_entry(entry)
+            except:
+                self._rollback_move(moved)
+                raise
+            moved.append(entry)
+
+    def _rollback_move(self, moved):
+        """Try to rollback a previous move in case of an filesystem error."""
+        inv = self.inventory
+        for entry in moved:
+            try:
+                self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
+                    entry.to_tail, entry.to_parent_id, entry.from_rel,
+                    entry.from_tail, entry.from_parent_id,
+                    entry.only_change_inv))
+            except OSError, e:
+                raise errors.BzrMoveFailedError( '', '', "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):
+        inv = self.inventory
+        from_rel_abs = self.abspath(entry.from_rel)
+        to_rel_abs = self.abspath(entry.to_rel)
+        if from_rel_abs == to_rel_abs:
+            raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
+                "Source and target are identical.")
+
+        if not entry.only_change_inv:
+            try:
+                osutils.rename(from_rel_abs, to_rel_abs)
+            except OSError, e:
+                raise errors.BzrMoveFailedError(entry.from_rel,
+                    entry.to_rel, e[1])
+        inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
 
     @needs_tree_write_lock
-    def rename_one(self, from_rel, to_rel):
+    def rename_one(self, from_rel, to_rel, after=False):
         """Rename one file.
 
         This can change the directory or the filename or both.
+
+        rename_one has several 'modes' to work. First, it can rename a physical
+        file and change the file_id. That is the normal mode. Second, it can
+        only change the file_id without touching any physical file. This is
+        the new mode introduced in version 0.15.
+
+        rename_one uses the second mode if 'after == True' and 'to_rel' is not
+        versioned but present in the working tree.
+
+        rename_one uses the second mode if 'after == False' and 'from_rel' is
+        versioned but no longer in the working tree, and 'to_rel' is not
+        versioned but present in the working tree.
+
+        rename_one uses the first mode if 'after == False' and 'from_rel' is
+        versioned and present in the working tree, and 'to_rel' is not
+        versioned and not present in the working tree.
+
+        Everything else results in an error.
         """
         inv = self.inventory
-        if not self.has_filename(from_rel):
-            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
-        if self.has_filename(to_rel):
-            raise BzrError("can't rename: new working file %r already exists" % to_rel)
-
-        file_id = inv.path2id(from_rel)
-        if file_id is None:
-            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
-
-        entry = inv[file_id]
-        from_parent = entry.parent_id
-        from_name = entry.name
-        
-        if inv.path2id(to_rel):
-            raise BzrError("can't rename: new name %r is already versioned" % to_rel)
-
+        rename_entries = []
+
+        # create rename entries and tuples
+        from_tail = splitpath(from_rel)[-1]
+        from_id = inv.path2id(from_rel)
+        if from_id is None:
+            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)
         to_dir_id = inv.path2id(to_dir)
-        if to_dir_id is None and to_dir != '':
-            raise BzrError("can't determine destination directory id for %r" % to_dir)
-
-        mutter("rename_one:")
-        mutter("  file_id    {%s}" % file_id)
-        mutter("  from_rel   %r" % from_rel)
-        mutter("  to_rel     %r" % to_rel)
-        mutter("  to_dir     %r" % to_dir)
-        mutter("  to_dir_id  {%s}" % to_dir_id)
-
-        inv.rename(file_id, to_dir_id, to_tail)
-
-        from_abs = self.abspath(from_rel)
-        to_abs = self.abspath(to_rel)
-        try:
-            osutils.rename(from_abs, to_abs)
-        except OSError, e:
-            inv.rename(file_id, from_parent, from_name)
-            raise BzrError("failed to rename %r to %r: %s"
-                    % (from_abs, to_abs, e[1]))
+        rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
+                                     from_id=from_id,
+                                     from_tail=from_tail,
+                                     from_parent_id=from_parent_id,
+                                     to_rel=to_rel, to_tail=to_tail,
+                                     to_parent_id=to_dir_id)
+        rename_entries.append(rename_entry)
+
+        # determine which move mode to use. checks also for movability
+        rename_entries = self._determine_mv_mode(rename_entries, after)
+
+        # check if the target changed directory and if the target directory is
+        # versioned
+        if to_dir_id is None:
+            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'
+               '  from_id   {%s}\n'
+               '  from_rel: %r\n'
+               '  to_rel:   %r\n'
+               '  to_dir    %r\n'
+               '  to_dir_id {%s}\n',
+               from_id, from_rel, to_rel, to_dir, to_dir_id)
+
+        self._move(rename_entries)
         self._write_inventory(inv)
 
+    class _RenameEntry(object):
+        def __init__(self, from_rel, from_id, from_tail, from_parent_id,
+                     to_rel, to_tail, to_parent_id, only_change_inv=False):
+            self.from_rel = from_rel
+            self.from_id = from_id
+            self.from_tail = from_tail
+            self.from_parent_id = from_parent_id
+            self.to_rel = to_rel
+            self.to_tail = to_tail
+            self.to_parent_id = to_parent_id
+            self.only_change_inv = only_change_inv
+
     @needs_read_lock
     def unknowns(self):
         """Return all unknown files.
@@ -1490,7 +1643,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):
@@ -1539,7 +1692,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
@@ -1720,10 +1873,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):
@@ -1803,7 +1956,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):
@@ -1834,13 +1987,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):
@@ -1908,7 +2061,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)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: enhancedmv5.patch.zip
Type: application/zip
Size: 358504 bytes
Desc: not available
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20070119/e33e9f1d/attachment-0001.zip 


More information about the bazaar mailing list