Rev 2455: bzr rm removes the working file in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Wed Apr 25 07:50:30 BST 2007
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 2455
revision-id: pqm at pqm.ubuntu.com-20070425065022-rsmpi4x2q1gn8536
parent: pqm at pqm.ubuntu.com-20070425054241-urh0t3nequwc2j6q
parent: amanic at gmail.com-20070420071526-8e395b3gqvp1by61
committer: Canonical.com Patch Queue Manager<pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2007-04-25 07:50:22 +0100
message:
bzr rm removes the working file
added:
bzrlib/tests/workingtree_implementations/test_remove.py test_remove.py-20070413183901-rvnp85rtc0q0sclp-1
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/delta.py delta.py-20050729221636-54cf14ef94783d0a
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_mv.py test_mv.py-20060705114902-33tkxz0o9cdshemo-1
bzrlib/tests/blackbox/test_remove.py test_remove.py-20060530011439-fika5rm84lon0goe-1
bzrlib/tests/blackbox/test_selftest.py test_selftest.py-20060123024542-01c5f1bbcb596d78
bzrlib/tests/blackbox/test_too_much.py blackbox.py-20050620052131-a7370d756399f615
bzrlib/tests/workingtree_implementations/__init__.py __init__.py-20060203003124-b2aa5aca21a8bfad
bzrlib/workingtree.py workingtree.py-20050511021032-29b6ec0a681e02e3
------------------------------------------------------------
revno: 2292.1.34
merged: amanic at gmail.com-20070420071526-8e395b3gqvp1by61
parent: amanic at gmail.com-20070420070001-lbknio8gvry1au0w
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-20 09:15:26 +0200
message:
Move "magically convert commands like 'remove abc' to ['remove', 'abc']"
from tests/__init__.run_bzr_captured to tests/blackbox/test_remove
------------------------------------------------------------
revno: 2292.1.33
merged: amanic at gmail.com-20070420070001-lbknio8gvry1au0w
parent: amanic at gmail.com-20070419055947-x776pmmp7uj7utva
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-20 09:00:01 +0200
message:
* Remove workingtree.canonicalpath
------------------------------------------------------------
revno: 2292.1.32
merged: amanic at gmail.com-20070419055947-x776pmmp7uj7utva
parent: amanic at gmail.com-20070418202208-8081vgmmyr6k87op
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-19 07:59:47 +0200
message:
* tests/__init__.run_bzr
Now gives better assert message for 'Unexpected return code'
* blackbox/test_remove
- Add changed file tests
- When checking changed/unknown rm fails, always wheck if we can forec it.
- Move one file tests to top
- Fixed copy-paste pattern induced mishaps where more-than-one-file-tests
were actually just testing one-file
- Add test to remove while in a child dir
- Add remove deleted file test
------------------------------------------------------------
revno: 2292.1.31
merged: amanic at gmail.com-20070418202208-8081vgmmyr6k87op
parent: amanic at gmail.com-20070418201215-upzz7pof6a4433n7
parent: pqm at pqm.ubuntu.com-20070417080415-5vn25svmf95ki88z
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Wed 2007-04-18 22:22:08 +0200
message:
Merge bzr.dev
------------------------------------------------------------
revno: 2292.1.30
merged: amanic at gmail.com-20070418201215-upzz7pof6a4433n7
parent: amanic at gmail.com-20070416183544-tdtramoul71svkhh
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Wed 2007-04-18 22:12:15 +0200
message:
* Minor text fixes.
* Remove trailing whitespace added by this branch.
* Add tests for removing empty directories.
* workingtree.remove
- Don't recurse into empty directories.
- Use absolute path when deleting from the file system.
------------------------------------------------------------
revno: 2292.1.29
merged: amanic at gmail.com-20070416183544-tdtramoul71svkhh
parent: amanic at gmail.com-20070416182641-8ewx7hfmkehjq5li
parent: pqm at pqm.ubuntu.com-20070416080254-bf3rfk77k5bgfdl7
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-16 20:35:44 +0200
message:
Merge bzr.dev
------------------------------------------------------------
revno: 2292.1.28
merged: amanic at gmail.com-20070416182641-8ewx7hfmkehjq5li
parent: amanic at gmail.com-20070416062659-z2yrpp5118a1yeyr
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-16 20:26:41 +0200
message:
* NEWS
- move my news to NOTES WHEN UPGRADING section.
* bzrlib/builtins.py
- Use RegistryOption for bzr rm --force and --keep
* bzrlib/tests/__init__.py
- remove a white space
* bzrlib/tests/blackbox/test_selftest.py
- make blackbox/test_selftest.py pass again:
Its run_bzr_captured method did not return anything, although bzr_run
expects it now.
------------------------------------------------------------
revno: 2292.1.27
merged: amanic at gmail.com-20070416062659-z2yrpp5118a1yeyr
parent: amanic at gmail.com-20070416025505-spmkhtjsrbiv21m3
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-16 08:26:59 +0200
message:
* tests/__init__.TestCase.run_bzr_captured
- Support single text commands with quoted sub-strings eg.:
"bzr commit 'some message'"
- Move support for error_regexes from run_bzr_error() to run_bzr()
as it is quite usefull to assert output from notes.
* blackbox/test_remove
- convert the rest of the tests to use the new improved self.run_bzr
------------------------------------------------------------
revno: 2292.1.26
merged: amanic at gmail.com-20070416025505-spmkhtjsrbiv21m3
parent: amanic at gmail.com-20070416010020-3jan8hje4qmbwdsh
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-16 04:55:05 +0200
message:
* tests/__init__
- make run_bzr_captured abit more clever, so that it will
split up argunents if they are passed as a single string.
* blackbox/test_remove
- start converting to run_bzr and run_bzr_error
------------------------------------------------------------
revno: 2292.1.25
merged: amanic at gmail.com-20070416010020-3jan8hje4qmbwdsh
parent: amanic at gmail.com-20070414124037-ewx6hp70tw4vdtdb
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-16 03:00:20 +0200
message:
* Add utility method delta.get_changes_as_text to get the output of .show()
as a string.
* Add new errors.BzrRemoveChangedFilesError to raise when 'bzr remove'
should refuse to delete stuff.
* Add workingtree.canicalpath(filename) to normalize file names.
* Changed the working of workingtree.remove(...) to check if any files
are changed before it starts deleting anything. Will raise exception
now if changed files are passed to be removed.
* workingtree_implementations/test_remove.py
- Checked all tests and add more cases.
------------------------------------------------------------
revno: 2292.1.24
merged: amanic at gmail.com-20070414124037-ewx6hp70tw4vdtdb
parent: amanic at gmail.com-20070414115726-kykcsm9pztykst1w
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Sat 2007-04-14 14:40:37 +0200
message:
minor text cleanups
------------------------------------------------------------
revno: 2292.1.23
merged: amanic at gmail.com-20070414115726-kykcsm9pztykst1w
parent: amanic at gmail.com-20070414005827-zf4n2y9scrjs2d2x
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Sat 2007-04-14 13:57:26 +0200
message:
Revert test_too_much.py and just do the minimum te get the tests to pass.
Add some test to test_remove.py.
Let test_remove.py make safe id's.
------------------------------------------------------------
revno: 2292.1.22
merged: amanic at gmail.com-20070414005827-zf4n2y9scrjs2d2x
parent: amanic at gmail.com-20070413194540-jaf9n9wzhx4pd66c
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Sat 2007-04-14 02:58:27 +0200
message:
Implement TODO: Normalize names.
Assume paths relative to the basedir.
(not to the current dir, which I did previously)
------------------------------------------------------------
revno: 2292.1.21
merged: amanic at gmail.com-20070413194540-jaf9n9wzhx4pd66c
parent: amanic at gmail.com-20070413184526-6xhpgyvqz2u18l2q
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-13 21:45:40 +0200
message:
undo tree.has_id fake-bugfix, which seems to not be needed anymore..
------------------------------------------------------------
revno: 2292.1.20
merged: amanic at gmail.com-20070413184526-6xhpgyvqz2u18l2q
parent: amanic at gmail.com-20070413182326-qt44rjuzo249l7x8
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-13 20:45:26 +0200
message:
move test_workingtree.TestRemove to workingtree_implementations/test_remove
------------------------------------------------------------
revno: 2292.1.19
merged: amanic at gmail.com-20070413182326-qt44rjuzo249l7x8
parent: amanic at gmail.com-20070413181313-btt9e9cx16987yrq
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-13 20:23:26 +0200
message:
convert from using self.runbzr to self.run_bzr_captured
------------------------------------------------------------
revno: 2292.1.18
merged: amanic at gmail.com-20070413181313-btt9e9cx16987yrq
parent: amanic at gmail.com-20070412014719-3oo573tt381rpigu
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-04-13 20:13:13 +0200
message:
revert wrapping change
------------------------------------------------------------
revno: 2292.1.17
merged: amanic at gmail.com-20070412014719-3oo573tt381rpigu
parent: amanic at gmail.com-20070412013832-1avmdh8pba8yp4b1
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 03:47:19 +0200
message:
Remove redundant __init__ and object variable.
------------------------------------------------------------
revno: 2292.1.16
merged: amanic at gmail.com-20070412013832-1avmdh8pba8yp4b1
parent: amanic at gmail.com-20070412011853-z30g2o1ypj7rqcyr
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 03:38:32 +0200
message:
Update NEWS
------------------------------------------------------------
revno: 2292.1.15
merged: amanic at gmail.com-20070412011853-z30g2o1ypj7rqcyr
parent: amanic at gmail.com-20070412011017-ofzhcgj7o7bnh6dd
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 03:18:53 +0200
message:
Fix test too much again.
------------------------------------------------------------
revno: 2292.1.14
merged: amanic at gmail.com-20070412011017-ofzhcgj7o7bnh6dd
parent: amanic at gmail.com-20070412004748-0nsa6sughf0xu744
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 03:10:17 +0200
message:
* blackbox/test_remove
- uninvert my logic
- add a little more testing
------------------------------------------------------------
revno: 2292.1.13
merged: amanic at gmail.com-20070412004748-0nsa6sughf0xu744
parent: amanic at gmail.com-20070411225348-7145vlsq3f390osg
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 02:47:48 +0200
message:
* merge the unversion command back into the remove command,
merging the commands and tests.
* make all tests pass again.
------------------------------------------------------------
revno: 2292.1.12
merged: amanic at gmail.com-20070411225348-7145vlsq3f390osg
parent: amanic at gmail.com-20070411220954-azjn3zpjystqowcv
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 00:53:48 +0200
message:
* test_workingtree/TestRemove
- Change global and local constants to class variables.
------------------------------------------------------------
revno: 2292.1.11
merged: amanic at gmail.com-20070411220954-azjn3zpjystqowcv
parent: amanic at gmail.com-20070411220247-plcjs3dvidvjx2vj
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 00:09:54 +0200
message:
* workingtree.remove
- Don't figure out what changed if we don't care.
------------------------------------------------------------
revno: 2292.1.10
merged: amanic at gmail.com-20070411220247-plcjs3dvidvjx2vj
parent: amanic at gmail.com-20070411212706-7jzaph3pv0856qks
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Thu 2007-04-12 00:02:47 +0200
message:
* workingtree.remove
- Add and test a new force option in order to force the deletion of changed files
and non-empty directories.
- Some doc fixups.
- Remove some implemented todo's
- Remove ## TODO: Remove nested loops; better scalability
as I can't see any nested loop.s
- small whitespace cleanups in tests.
------------------------------------------------------------
revno: 2292.1.9
merged: amanic at gmail.com-20070411212706-7jzaph3pv0856qks
parent: amanic at gmail.com-20070411204847-quynsdasuvjhju64
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Wed 2007-04-11 23:27:06 +0200
message:
Handle the removing of nonempty directories.
------------------------------------------------------------
revno: 2292.1.8
merged: amanic at gmail.com-20070411204847-quynsdasuvjhju64
parent: amanic at gmail.com-20070409215050-9k4jxh76grq7jxz8
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Wed 2007-04-11 22:48:47 +0200
message:
When removing and deleting files,
only check for changes on specific files.
------------------------------------------------------------
revno: 2292.1.7
merged: amanic at gmail.com-20070409215050-9k4jxh76grq7jxz8
parent: amanic at gmail.com-20070331204906-iai2v10wk3l3lt12
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-04-09 23:50:50 +0200
message:
First pass at only deleting files on 'bzr remove' when
they haven't changed.
Needed to modify _find_children_across_trees (tree.py) because
tree.has_id did not work as excpeted.
------------------------------------------------------------
revno: 2292.1.6
merged: amanic at gmail.com-20070331204906-iai2v10wk3l3lt12
parent: amanic at gmail.com-20070323182956-5vestftm6gx3o9mz
parent: pqm at pqm.ubuntu.com-20070329064515-7bfc20fbcf9cf1a7
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Sat 2007-03-31 22:49:06 +0200
message:
merge bzr.dev
------------------------------------------------------------
revno: 2292.1.5
merged: amanic at gmail.com-20070323182956-5vestftm6gx3o9mz
parent: amanic at gmail.com-20070219054605-o69te1fuwcirv01a
parent: pqm at pqm.ubuntu.com-20070322230820-f8735ba918f51539
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-03-23 20:29:56 +0200
message:
merge with bzr.dev
------------------------------------------------------------
revno: 2292.1.4
merged: amanic at gmail.com-20070219054605-o69te1fuwcirv01a
parent: amanic at gmail.com-20070216072443-g38i13amiri6j2ir
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Mon 2007-02-19 07:46:05 +0200
message:
Make output for bzr remove more verbose.
------------------------------------------------------------
revno: 2292.1.3
merged: amanic at gmail.com-20070216072443-g38i13amiri6j2ir
parent: amanic at gmail.com-20070216071540-wq20se0crnnki9c9
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-02-16 09:24:43 +0200
message:
Factored out common code from test_remove.py into test_unversion.py
------------------------------------------------------------
revno: 2292.1.2
merged: amanic at gmail.com-20070216071540-wq20se0crnnki9c9
parent: amanic at gmail.com-20070216061611-sjscmgi4v5rozq6h
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-02-16 09:15:40 +0200
message:
Minor test cleanups, making test_remove.py and test_unversion.py more similar.
------------------------------------------------------------
revno: 2292.1.1
merged: amanic at gmail.com-20070216061611-sjscmgi4v5rozq6h
parent: pqm at pqm.ubuntu.com-20070215181416-864dbe690a0f3da8
committer: Marius Kruger <amanic at gmail.com>
branch nick: bzr.rm_delete_working_file
timestamp: Fri 2007-02-16 08:16:11 +0200
message:
"bzr remove" and "bzr rm" will now remove the working file.
This has been done for consistency with svn and the unix rm command.
The old remove behaviour has been retained in the new command
"bzr unversion", which will just stop versioning the file,
but not delete it.
(Addressing Bug #82602)
Exisitng tests have been reworked and new tests were added to test these
changes properly.
=== added file 'bzrlib/tests/workingtree_implementations/test_remove.py'
--- a/bzrlib/tests/workingtree_implementations/test_remove.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_remove.py 2007-04-18 20:12:15 +0000
@@ -0,0 +1,205 @@
+# Copyright (C) 2006, 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Tests for interface conformance of 'WorkingTree.remove'"""
+
+import re
+from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
+from bzrlib import errors, osutils
+
+class TestRemove(TestCaseWithWorkingTree):
+ """Tests WorkingTree.remove"""
+
+ files=['a', 'b/', 'b/c', 'd/']
+ a = ['a']
+ b = ['b']
+ b_c = ['b', 'b/c']
+
+ def getTree(self):
+ self.makeAndChdirToTestDir()
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(TestRemove.files)
+ return tree
+
+ def test_remove_keep(self):
+ """Check that files are unversioned but not deleted."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ self.assertInWorkingTree(TestRemove.files)
+
+ tree.remove(TestRemove.files)
+ self.assertNotInWorkingTree(TestRemove.files)
+ self.failUnlessExists(TestRemove.files)
+
+ def test_remove_unchanged_files(self):
+ """Check that unchanged files are removed and deleted."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ tree.commit("files must not have changes")
+ self.assertInWorkingTree(TestRemove.files)
+
+ tree.remove(TestRemove.files, keep_files=False)
+
+ self.assertNotInWorkingTree(TestRemove.files)
+ self.failIfExists(TestRemove.files)
+
+ def test_remove_added_files(self):
+ """Removal of newly added files must fail."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ self.assertInWorkingTree(TestRemove.files)
+ try:
+ tree.remove(TestRemove.files, keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*added:.*a.*b.*b/c.*d.*',
+ str(e), re.DOTALL))
+ self.assertInWorkingTree(TestRemove.files)
+ self.failUnlessExists(TestRemove.files)
+
+ def test_remove_changed_file(self):
+ """Removal of a changed files must fail."""
+ tree = self.getTree()
+ tree.add(TestRemove.a)
+ tree.commit("make sure a is versioned")
+ f = file('a', 'wb')
+ f.write("some other new content!")
+ f.close()
+ self.assertInWorkingTree(TestRemove.a)
+ try:
+ tree.remove(TestRemove.a, keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*modified:.*a.*',
+ str(e), re.DOTALL))
+ self.assertInWorkingTree(TestRemove.a)
+ self.failUnlessExists(TestRemove.a)
+
+ def test_remove_deleted_files(self):
+ """Check that files are removed if they don't exist any more."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ tree.commit("make sure files are versioned")
+ for f in ['b/c', 'b', 'a', 'd']:
+ osutils.delete_any(f)
+ self.assertInWorkingTree(TestRemove.files)
+ self.failIfExists(TestRemove.files)
+
+ tree.remove(TestRemove.files, keep_files=False)
+
+ self.assertNotInWorkingTree(TestRemove.files)
+ self.failIfExists(TestRemove.files)
+
+ def test_force_remove_changed_files(self):
+ """Check that changed files are removed and deleted when forced."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ self.assertInWorkingTree(TestRemove.files)
+
+ tree.remove(TestRemove.files, keep_files=False, force=True)
+
+ self.assertNotInWorkingTree(TestRemove.files)
+ self.failIfExists(TestRemove.files)
+
+ def test_remove_unknown_files(self):
+ """Try to delete unknown files."""
+ tree = self.getTree()
+ try:
+ tree.remove(TestRemove.files, keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*unknown:.*b/c.*b.*a.*d.*',
+ str(e), re.DOTALL))
+
+ def test_remove_nonexisting_files(self):
+ """Try to delete non-existing files."""
+ tree = self.getTree()
+ tree.remove([''], keep_files=False)
+ try:
+ tree.remove(['xyz', 'abc/def'], keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*unknown:.*xyz.*abc/def.*',
+ str(e), re.DOTALL))
+
+ def test_remove_nonempty_directory(self):
+ """Unchanged non-empty directories should be deleted."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ tree.commit("make sure b is versioned")
+ self.assertInWorkingTree(TestRemove.files)
+ self.failUnlessExists(TestRemove.files)
+ tree.remove(TestRemove.b, keep_files=False)
+ self.assertNotInWorkingTree(TestRemove.b)
+ self.failIfExists(TestRemove.b)
+
+ def test_remove_nonempty_directory_with_unknowns(self):
+ """Unchanged non-empty directories should be deleted."""
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ tree.commit("make sure b is versioned")
+ self.assertInWorkingTree(TestRemove.files)
+ self.failUnlessExists(TestRemove.files)
+ f = file('b/my_unknown_file', 'wb')
+ f.write("some content!")
+ f.close()
+ try:
+ tree.remove(TestRemove.b, keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*unknown:.*b/my_unknown_file.*',
+ str(e), re.DOTALL))
+ self.assertInWorkingTree(TestRemove.b)
+ self.failUnlessExists(TestRemove.b)
+
+ def test_force_remove_nonempty_directory(self):
+ tree = self.getTree()
+ tree.add(TestRemove.files)
+ tree.commit("make sure b is versioned")
+ self.assertInWorkingTree(TestRemove.files)
+ self.failUnlessExists(TestRemove.files)
+ tree.remove(TestRemove.b, keep_files=False, force=True)
+ self.assertNotInWorkingTree(TestRemove.b_c)
+ self.failIfExists(TestRemove.b_c)
+
+ def test_remove_directory_with_changed_file(self):
+ """Refuse to delete directories with changed files."""
+ tree = self.getTree()
+ tree.add(TestRemove.b_c)
+ tree.commit("make sure b and c are versioned")
+ f = file('b/c', 'wb')
+ f.write("some other new content!")
+ f.close()
+ self.assertInWorkingTree(TestRemove.b_c)
+ try:
+ tree.remove(TestRemove.b, keep_files=False)
+ self.fail('Should throw BzrRemoveChangedFilesError')
+ except errors.BzrRemoveChangedFilesError, e:
+ self.assertTrue(re.match('Can\'t remove changed or unknown files:'
+ '.*modified:.*b/c.*',
+ str(e), re.DOTALL))
+ self.assertInWorkingTree(TestRemove.b_c)
+ self.failUnlessExists(TestRemove.b_c)
+
+ #see if we can force it now..
+ tree.remove(TestRemove.b, keep_files=False, force=True)
+ self.assertNotInWorkingTree(TestRemove.b_c)
+ self.failIfExists(TestRemove.b_c)
=== modified file 'NEWS'
--- a/NEWS 2007-04-24 22:44:26 +0000
+++ b/NEWS 2007-04-25 06:50:22 +0000
@@ -1,5 +1,18 @@
IN DEVELOPMENT
+ NOTES WHEN UPGRADING:
+
+ * ``bzr remove`` and ``bzr rm`` will now remove the working file, if
+ it could be recovered again.
+ This has been done for consistency with svn and the unix rm command.
+ The old ``remove`` behaviour has been retained in the new option
+ ``bzr remove --keep``, which will just stop versioning the file,
+ but not delete it.
+ ``bzr remove --force`` have been added which will always delete the
+ files.
+ ``bzr remove`` is also more verbose.
+ (Marius Kruger, #82602)
+
IMPROVEMENTS:
* Merge directives can now be supplied as input to `merge` and `pull`,
@@ -2088,7 +2101,6 @@
* Simplify handling of DivergedBranches in cmd_pull().
(Michael Ellerman)
-
* Branch.controlfile* logic has moved to lockablefiles.LockableFiles, which
is exposed as Branch().control_files. Also this has been altered with the
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2007-04-24 02:43:56 +0000
+++ b/bzrlib/builtins.py 2007-04-25 06:50:22 +0000
@@ -1074,10 +1074,10 @@
class cmd_remove(Command):
- """Make a file unversioned.
+ """Remove files or directories.
- This makes bzr stop tracking changes to a versioned file. It does
- not delete the working copy.
+ This makes bzr stop tracking changes to the specified files and
+ delete them if they can easily be recovered using revert.
You can specify one or more files, and/or --new. If you specify --new,
only 'added' files will be removed. If you specify both, then new files
@@ -1085,23 +1085,38 @@
also new, they will also be removed.
"""
takes_args = ['file*']
- takes_options = ['verbose', Option('new', help='remove newly-added files')]
+ takes_options = ['verbose',
+ Option('new', help='remove newly-added files'),
+ RegistryOption.from_kwargs('file-deletion-strategy',
+ 'The file deletion mode to be used',
+ title='Deletion Strategy', value_switches=True, enum_switch=False,
+ safe='Only delete files if they can be'
+ ' safely recovered (default).',
+ keep="Don't delete any files.",
+ force='Delete all the specified files, even if they can not be '
+ 'recovered and even if they are non-empty directories.')]
aliases = ['rm']
encoding_type = 'replace'
-
- def run(self, file_list, verbose=False, new=False):
+
+ def run(self, file_list, verbose=False, new=False,
+ file_deletion_strategy='safe'):
tree, file_list = tree_files(file_list)
- if new is False:
- if file_list is None:
- raise errors.BzrCommandError('Specify one or more files to'
- ' remove, or use --new.')
- else:
+
+ if file_list is not None:
+ file_list = [f for f in file_list if f != '']
+ elif not new:
+ raise errors.BzrCommandError('Specify one or more files to'
+ ' remove, or use --new.')
+
+ if new:
added = tree.changes_from(tree.basis_tree(),
specific_files=file_list).added
file_list = sorted([f[0] for f in added], reverse=True)
if len(file_list) == 0:
raise errors.BzrCommandError('No matching files.')
- tree.remove(file_list, verbose=verbose, to_file=self.outf)
+ tree.remove(file_list, verbose=verbose, to_file=self.outf,
+ keep_files=file_deletion_strategy=='keep',
+ force=file_deletion_strategy=='force')
class cmd_file_id(Command):
=== modified file 'bzrlib/delta.py'
--- a/bzrlib/delta.py 2007-03-07 23:15:10 +0000
+++ b/bzrlib/delta.py 2007-04-16 01:00:20 +0000
@@ -195,6 +195,12 @@
print >>to_file, 'unknown:'
show_list(self.unversioned)
+ def get_changes_as_text(self, show_ids=False, show_unchanged=False,
+ short_status=False):
+ import StringIO
+ output = StringIO.StringIO()
+ self.show(output, show_ids, show_unchanged, short_status)
+ return output.getvalue()
@deprecated_function(zero_nine)
def compare_trees(old_tree, new_tree, want_unchanged=False,
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2007-04-23 06:55:58 +0000
+++ b/bzrlib/errors.py 2007-04-25 06:50:22 +0000
@@ -1633,6 +1633,18 @@
def __init__(self, from_path, to_path, extra=None):
BzrMoveFailedError.__init__(self, from_path, to_path, extra)
+class BzrRemoveChangedFilesError(BzrError):
+ """Used when user is trying to remove changed files."""
+
+ _fmt = ("Can't remove changed or unknown files:\n%(changes_as_text)s"
+ "Use --keep to not delete them, or --force to delete them regardless.")
+
+ def __init__(self, tree_delta):
+ BzrError.__init__(self)
+ self.changes_as_text = tree_delta.get_changes_as_text()
+ #self.paths_as_string = '\n'.join(changed_files)
+ #self.paths_as_string = '\n'.join([quotefn(p) for p in changed_files])
+
class BzrBadParameterNotString(BzrBadParameter):
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2007-04-25 02:24:56 +0000
+++ b/bzrlib/tests/__init__.py 2007-04-25 06:50:22 +0000
@@ -36,7 +36,6 @@
from pprint import pformat
import random
import re
-import shlex
import stat
from subprocess import Popen, PIPE
import sys
@@ -54,6 +53,7 @@
progress,
ui,
urlutils,
+ workingtree,
)
import bzrlib.branch
import bzrlib.commands
@@ -1294,7 +1294,8 @@
if err:
self.log('errors:\n%r', err)
if retcode is not None:
- self.assertEquals(retcode, result)
+ self.assertEquals(retcode, result,
+ message='Unexpected return code')
return out, err
def run_bzr(self, *args, **kwargs):
@@ -1315,8 +1316,15 @@
encoding = kwargs.pop('encoding', None)
stdin = kwargs.pop('stdin', None)
working_dir = kwargs.pop('working_dir', None)
- return self.run_bzr_captured(args, retcode=retcode, encoding=encoding,
- stdin=stdin, working_dir=working_dir)
+ error_regexes = kwargs.pop('error_regexes', [])
+
+ out, err = self.run_bzr_captured(args, retcode=retcode,
+ encoding=encoding, stdin=stdin, working_dir=working_dir)
+
+ for regex in error_regexes:
+ self.assertContainsRe(err, regex)
+ return out, err
+
def run_bzr_decode(self, *args, **kwargs):
if 'encoding' in kwargs:
@@ -1349,9 +1357,7 @@
'commit', '--strict', '-m', 'my commit comment')
"""
kwargs.setdefault('retcode', 3)
- out, err = self.run_bzr(*args, **kwargs)
- for regex in error_regexes:
- self.assertContainsRe(err, regex)
+ out, err = self.run_bzr(error_regexes=error_regexes, *args, **kwargs)
return out, err
def run_bzr_subprocess(self, *args, **kwargs):
@@ -1953,12 +1959,41 @@
self.assertEqualDiff(content, s)
def failUnlessExists(self, path):
- """Fail unless path, which may be abs or relative, exists."""
- self.failUnless(osutils.lexists(path),path+" does not exist")
+ """Fail unless path or paths, which may be abs or relative, exist."""
+ if not isinstance(path, basestring):
+ for p in path:
+ self.failUnlessExists(p)
+ else:
+ 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),path+" exists")
+ """Fail if path or paths, which may be abs or relative, exist."""
+ if not isinstance(path, basestring):
+ for p in path:
+ self.failIfExists(p)
+ else:
+ self.failIf(osutils.lexists(path),path+" exists")
+
+ def assertInWorkingTree(self,path,root_path='.',tree=None):
+ """Assert whether path or paths are in the WorkingTree"""
+ if tree is None:
+ tree = workingtree.WorkingTree.open(root_path)
+ if not isinstance(path, basestring):
+ for p in path:
+ self.assertInWorkingTree(p,tree=tree)
+ else:
+ self.assertIsNot(tree.path2id(path), None,
+ path+' not in working tree.')
+
+ def assertNotInWorkingTree(self,path,root_path='.',tree=None):
+ """Assert whether path or paths are not in the WorkingTree"""
+ if tree is None:
+ tree = workingtree.WorkingTree.open(root_path)
+ if not isinstance(path, basestring):
+ for p in path:
+ self.assertNotInWorkingTree(p,tree=tree)
+ else:
+ self.assertIs(tree.path2id(path), None, path+' in working tree.')
class TestCaseWithTransport(TestCaseInTempDir):
=== modified file 'bzrlib/tests/blackbox/test_mv.py'
--- a/bzrlib/tests/blackbox/test_mv.py 2007-03-07 01:14:11 +0000
+++ b/bzrlib/tests/blackbox/test_mv.py 2007-03-23 18:29:56 +0000
@@ -31,15 +31,6 @@
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)
=== modified file 'bzrlib/tests/blackbox/test_remove.py'
--- a/bzrlib/tests/blackbox/test_remove.py 2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/blackbox/test_remove.py 2007-04-20 07:15:26 +0000
@@ -15,48 +15,204 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import os
+import os, re, shlex
from bzrlib.tests.blackbox import ExternalBase
from bzrlib.workingtree import WorkingTree
+from bzrlib import osutils
+
+_id='-id'
+a='a'
+b='b/'
+c='b/c'
+d='d/'
+files=(a, b, c, d)
class TestRemove(ExternalBase):
- def test_remove_deleted(self):
- self.runbzr("init")
- self.build_tree(['a'])
- self.runbzr(['add', 'a'])
- self.runbzr(['commit', '-m', 'added a'])
- os.unlink('a')
- self.runbzr(['remove', 'a'])
-
- def test_remove_new(self):
- self.build_tree(['filefile',
- 'dir/',
- 'dir/filefilefile'])
- wt = self.make_branch_and_tree('.')
- wt.add(['filefile', 'dir', 'dir/filefilefile'],
- ['filefile-id', 'dir-id', 'filefilefile-id'])
- self.assertEqual(wt.path2id('filefile'), 'filefile-id')
- self.assertEqual(wt.path2id('dir/filefilefile'), 'filefilefile-id')
- self.assertEqual(wt.path2id('dir'), 'dir-id')
- self.runbzr('remove --new')
- wt = WorkingTree.open('.')
- self.assertIs(wt.path2id('filefile'), None)
- self.assertIs(wt.path2id('dir/filefilefile'), None)
- self.assertIs(wt.path2id('dir'), None)
- wt.add(['filefile', 'dir', 'dir/filefilefile'],
- ['filefile-id', 'dir-id', 'filefilefile-id'])
- self.assertEqual(wt.path2id('filefile'), 'filefile-id')
- self.assertEqual(wt.path2id('dir/filefilefile'), 'filefilefile-id')
- self.assertEqual(wt.path2id('dir'), 'dir-id')
- self.runbzr('remove --new dir')
- wt = WorkingTree.open('.')
- self.assertEqual(wt.path2id('filefile'), 'filefile-id')
- self.assertIs(wt.path2id('dir/filefilefile'), None)
- self.assertIs(wt.path2id('dir'), None)
- self.runbzr('remove --new .')
- wt = WorkingTree.open('.')
- self.assertIs(wt.path2id('filefile'), None)
- self.runbzr('remove --new .', retcode=3)
+ def run_bzr_captured(self, argv, retcode=0, encoding=None, stdin=None,
+ working_dir=None):
+
+ # magically convert commands like 'remove abc' to ['remove', 'abc']
+ if (isinstance(argv, tuple) and len(argv) == 1 and
+ isinstance(argv[0], basestring)):
+ argv = shlex.split(argv[0])
+ return ExternalBase.run_bzr_captured(self, argv, retcode, encoding,
+ stdin, working_dir)
+
+ def _make_add_and_assert_tree(self, files):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(files)
+ for f in files:
+ id=str(f).replace('/', '_') + _id
+ tree.add(f, id)
+ self.assertEqual(tree.path2id(f), id)
+ self.failUnlessExists(f)
+ self.assertInWorkingTree(f)
+ return tree
+
+ def assertFilesDeleted(self, files):
+ for f in files:
+ id=f+_id
+ self.assertNotInWorkingTree(f)
+ self.failIfExists(f)
+
+ def assertFilesUnversioned(self, files):
+ for f in files:
+ self.assertNotInWorkingTree(f)
+ self.failUnlessExists(f)
+
+ def changeFile(self, file_name):
+ f = file(file_name, 'ab')
+ f.write("\nsome other new content!")
+ f.close()
+
+ def run_bzr_remove_changed_files(self, error_regexes, files_to_remove):
+ error_regexes.extend(["Can't remove changed or unknown files:",
+ 'Use --keep to not delete them,'
+ ' or --force to delete them regardless.'
+ ])
+ self.run_bzr_error(error_regexes,
+ 'remove ' + ' '.join(files_to_remove))
+ #see if we can force it now
+ self.run_bzr('remove --force ' + ' '.join(files_to_remove))
+
+ def test_remove_no_files_specified(self):
+ tree = self._make_add_and_assert_tree([])
+ self.run_bzr_error(["bzr: ERROR: Specify one or more files to remove, "
+ "or use --new."], 'remove')
+
+ self.run_bzr_error(["bzr: ERROR: No matching files."], 'remove --new')
+
+ self.run_bzr_error(["bzr: ERROR: No matching files."],
+ 'remove --new .')
+
+ def test_rm_one_file(self):
+ tree = self._make_add_and_assert_tree([a])
+ self.run_bzr("commit -m 'added a'")
+ self.run_bzr('rm a', error_regexes=["deleted a"])
+ self.assertFilesDeleted([a])
+
+ def test_remove_one_file(self):
+ tree = self._make_add_and_assert_tree([a])
+ self.run_bzr("commit -m 'added a'")
+ self.run_bzr('remove a', error_regexes=["deleted a"])
+ self.assertFilesDeleted([a])
+
+ def test_remove_keep_one_file(self):
+ tree = self._make_add_and_assert_tree([a])
+ self.run_bzr('remove --keep a', error_regexes=["removed a"])
+ self.assertFilesUnversioned([a])
+
+ def test_remove_one_deleted_file(self):
+ tree = self._make_add_and_assert_tree([a])
+ self.run_bzr("commit -m 'added a'")
+ os.unlink(a)
+ self.assertInWorkingTree(a)
+ self.run_bzr('remove a')
+ self.assertNotInWorkingTree(a)
+
+ def test_remove_invalid_files(self):
+ self.build_tree(files)
+ tree = self.make_branch_and_tree('.')
+ self.run_bzr_remove_changed_files(['unknown:[.\s]*xyz[.\s]*abc/def'],
+ ['.', 'xyz', 'abc/def'])
+
+ def test_remove_unversioned_files(self):
+ self.build_tree(files)
+ tree = self.make_branch_and_tree('.')
+ self.run_bzr_remove_changed_files(
+ ['unknown:[.\s]*d/[.\s]*b/c[.\s]*b/[.\s]*a'], files)
+
+ def test_remove_changed_files(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr("commit -m 'added files'")
+ self.changeFile(a)
+ self.changeFile(c)
+ self.run_bzr_remove_changed_files(['modified:[.\s]*a[.\s]*b/c'], files)
+
+ def test_remove_changed_files_from_child_dir(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr("commit -m 'added files'")
+ self.changeFile(a)
+ self.changeFile(c)
+ os.chdir('b')
+ self.run_bzr_remove_changed_files(['modified:[.\s]*a[.\s]*b/c'],
+ ['../a', 'c', '.', '../d'])
+ os.chdir('..')
+ self.assertNotInWorkingTree(files)
+ self.failIfExists(files)
+
+ def test_remove_keep_unversioned_files(self):
+ self.build_tree(files)
+ tree = self.make_branch_and_tree('.')
+ self.run_bzr('remove --keep a', error_regexes=["a is not versioned."])
+ self.assertFilesUnversioned(files)
+
+ def test_remove_force_unversioned_files(self):
+ self.build_tree(files)
+ tree = self.make_branch_and_tree('.')
+ self.run_bzr('remove --force ' + ' '.join(files),
+ error_regexes=["deleted a", "deleted b",
+ "deleted b/c", "deleted d"])
+ self.assertFilesDeleted(files)
+
+ def test_remove_deleted_files(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr("commit -m 'added files'")
+ my_files=[f for f in files]
+ my_files.sort(reverse=True)
+ for f in my_files:
+ osutils.delete_any(f)
+ self.assertInWorkingTree(files)
+ self.failIfExists(files)
+ self.run_bzr('remove ' + ' '.join(files))
+ self.assertNotInWorkingTree(a)
+ self.failIfExists(files)
+
+ def test_remove_non_existing_files(self):
+ tree = self._make_add_and_assert_tree([])
+ self.run_bzr_remove_changed_files(['unknown:[.\s]*b'], ['b'])
+
+ def test_remove_keep_non_existing_files(self):
+ tree = self._make_add_and_assert_tree([])
+ self.run_bzr('remove --keep b', error_regexes=["b is not versioned."])
+
+ def test_remove_files(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr("commit -m 'added files'")
+ self.run_bzr('remove a b b/c d',
+ error_regexes=["deleted a", "deleted b", "deleted b/c",
+ "deleted d"])
+ self.assertFilesDeleted(files)
+
+ def test_remove_keep_files(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr("commit -m 'added files'")
+ self.run_bzr('remove --keep a b b/c d',
+ error_regexes=["removed a", "removed b", "removed b/c",
+ "removed d"])
+ self.assertFilesUnversioned(files)
+
+ def test_remove_with_new(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr('remove --new --keep',
+ error_regexes=["removed a", "removed b", "removed b/c"])
+ self.assertFilesUnversioned(files)
+
+ def test_remove_with_new_in_dir1(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr('remove --new --keep b b/c',
+ error_regexes=["removed b", "removed b/c"])
+ tree = WorkingTree.open('.')
+ self.assertInWorkingTree(a)
+ self.assertEqual(tree.path2id(a), a + _id)
+ self.assertFilesUnversioned([b,c])
+
+ def test_remove_with_new_in_dir2(self):
+ tree = self._make_add_and_assert_tree(files)
+ self.run_bzr('remove --new --keep .',
+ error_regexes=["removed a", "removed b", "removed b/c"])
+ tree = WorkingTree.open('.')
+ self.assertFilesUnversioned(files)
=== modified file 'bzrlib/tests/blackbox/test_selftest.py'
--- a/bzrlib/tests/blackbox/test_selftest.py 2007-04-13 00:30:33 +0000
+++ b/bzrlib/tests/blackbox/test_selftest.py 2007-04-25 06:50:22 +0000
@@ -103,6 +103,7 @@
self.encoding = encoding
self.stdin = stdin
self.working_dir = working_dir
+ return '', ''
def test_args(self):
"""Test that run_bzr passes args correctly to run_bzr_captured"""
=== modified file 'bzrlib/tests/blackbox/test_too_much.py'
--- a/bzrlib/tests/blackbox/test_too_much.py 2007-04-23 01:41:05 +0000
+++ b/bzrlib/tests/blackbox/test_too_much.py 2007-04-25 06:50:22 +0000
@@ -522,13 +522,13 @@
self.assertEquals(self.capture("relpath d2/link1"), "d2/link1\n")
runbzr(['commit', '-m', '4: retarget of two links'])
- runbzr('remove d2/link1')
+ runbzr('remove --keep d2/link1')
self.assertEquals(self.capture('unknowns'), 'd2/link1\n')
runbzr(['commit', '-m', '5: remove d2/link1'])
# try with the rm alias
runbzr('add d2/link1')
runbzr(['commit', '-m', '6: add d2/link1'])
- runbzr('rm d2/link1')
+ runbzr('rm --keep d2/link1')
self.assertEquals(self.capture('unknowns'), 'd2/link1\n')
runbzr(['commit', '-m', '7: remove d2/link1'])
=== modified file 'bzrlib/tests/workingtree_implementations/__init__.py'
--- a/bzrlib/tests/workingtree_implementations/__init__.py 2007-04-11 22:14:59 +0000
+++ b/bzrlib/tests/workingtree_implementations/__init__.py 2007-04-18 20:12:15 +0000
@@ -74,6 +74,7 @@
'bzrlib.tests.workingtree_implementations.test_put_file',
'bzrlib.tests.workingtree_implementations.test_readonly',
'bzrlib.tests.workingtree_implementations.test_read_working_inventory',
+ 'bzrlib.tests.workingtree_implementations.test_remove',
'bzrlib.tests.workingtree_implementations.test_rename_one',
'bzrlib.tests.workingtree_implementations.test_revision_tree',
'bzrlib.tests.workingtree_implementations.test_set_root_id',
=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py 2007-04-17 12:20:24 +0000
+++ b/bzrlib/workingtree.py 2007-04-25 06:50:22 +0000
@@ -367,7 +367,7 @@
def abspath(self, filename):
return pathjoin(self.basedir, filename)
-
+
def basis_tree(self):
"""Return RevisionTree for the current last revision.
@@ -1767,43 +1767,97 @@
return result
@needs_tree_write_lock
- def remove(self, files, verbose=False, to_file=None):
- """Remove nominated files from the working inventory..
-
- This does not remove their text. This does not run on XXX on what? RBC
-
- TODO: Refuse to remove modified files unless --force is given?
-
- TODO: Do something useful with directories.
-
- TODO: Should this remove the text or not? Tough call; not
- removing may be useful and the user can just use use rm, and
- is the opposite of add. Removing it is consistent with most
- other tools. Maybe an option.
+ def remove(self, files, verbose=False, to_file=None, keep_files=True,
+ force=False):
+ """Remove nominated files from the working inventor.
+
+ :files: File paths relative to the basedir.
+ :keep_files: If true, the files will also be kept.
+ :force: Delete files and directories, even if they are changed and
+ even if the directories are not empty.
"""
## TODO: Normalize names
- ## TODO: Remove nested loops; better scalability
+
if isinstance(files, basestring):
files = [files]
inv = self.inventory
+ new_files=set()
+ unknown_files_in_directory=set()
+
+ def recurse_directory_to_add_files(directory):
+ # recurse directory and add all files
+ # so we can check if they have changed.
+ for contained_dir_info in self.walkdirs(directory):
+ for file_info in contained_dir_info[1]:
+ if file_info[2] == 'file':
+ relpath = self.relpath(file_info[0])
+ if file_info[4]: #is it versioned?
+ new_files.add(relpath)
+ else:
+ unknown_files_in_directory.add(
+ (relpath, None, file_info[2]))
+
+ for filename in files:
+ # Get file name into canonical form.
+ filename = self.relpath(self.abspath(filename))
+ if len(filename) > 0:
+ new_files.add(filename)
+ if osutils.isdir(filename) and len(os.listdir(filename)) > 0:
+ recurse_directory_to_add_files(filename)
+ files = [f for f in new_files]
+
+ # Sort needed to first handle directory content before the directory
+ files.sort(reverse=True)
+ if not keep_files and not force:
+ tree_delta = self.changes_from(self.basis_tree(),
+ specific_files=files)
+ for unknown_file in unknown_files_in_directory:
+ tree_delta.unversioned.extend((unknown_file,))
+ if bool(tree_delta.modified
+ or tree_delta.added
+ or tree_delta.renamed
+ or tree_delta.kind_changed
+ or tree_delta.unversioned):
+ raise errors.BzrRemoveChangedFilesError(tree_delta)
+
# do this before any modifications
for f in files:
fid = inv.path2id(f)
+ message=None
if not fid:
- note("%s is not versioned."%f)
+ message="%s is not versioned." % (f,)
else:
if verbose:
- # having remove it, it must be either ignored or unknown
+ # having removed it, it must be either ignored or unknown
if self.is_ignored(f):
new_status = 'I'
else:
new_status = '?'
textui.show_status(new_status, inv[fid].kind, f,
to_file=to_file)
+ # unversion file
del inv[fid]
-
+ message="removed %s" % (f,)
+
+ if not keep_files:
+ abs_path = self.abspath(f)
+ if osutils.lexists(abs_path):
+ if (osutils.isdir(abs_path) and
+ len(os.listdir(abs_path)) > 0):
+ message="%s is not empty directory "\
+ "and won't be deleted." % (f,)
+ else:
+ osutils.delete_any(abs_path)
+ message="deleted %s" % (f,)
+ elif message is not None:
+ # only care if we haven't done anything yet.
+ message="%s does not exist." % (f,)
+
+ # print only one message (if any) per file.
+ if message is not None:
+ note(message)
self._write_inventory(inv)
@needs_tree_write_lock
@@ -2106,11 +2160,16 @@
def walkdirs(self, prefix=""):
"""Walk the directories of this tree.
+ returns a generator which yields items in the form:
+ ((curren_directory_path, fileid),
+ [(file1_path, file1_name, file1_kind, (lstat), file1_id,
+ file1_kind), ... ])
+
This API returns a generator, which is only valid during the current
tree transaction - within a single lock_read or lock_write duration.
- If the tree is not locked, it may cause an error to be raised, depending
- on the tree implementation.
+ If the tree is not locked, it may cause an error to be raised,
+ depending on the tree implementation.
"""
disk_top = self.abspath(prefix)
if disk_top.endswith('/'):
@@ -2208,6 +2267,14 @@
disk_finished = True
def _walkdirs(self, prefix=""):
+ """Walk the directories of this tree.
+
+ :prefix: is used as the directrory to start with.
+ returns a generator which yields items in the form:
+ ((curren_directory_path, fileid),
+ [(file1_path, file1_name, file1_kind, None, file1_id,
+ file1_kind), ... ])
+ """
_directory = 'directory'
# get the root in the inventory
inv = self.inventory
More information about the bazaar-commits
mailing list