Rev 3637: (robertc) Improve bzr rm to detect missing files and have an alias to in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Fri Aug 15 06:40:17 BST 2008
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 3637
revision-id: pqm at pqm.ubuntu.com-20080815054008-1xrwm20d1wv9fyt7
parent: pqm at pqm.ubuntu.com-20080815025810-eguaiqf0kwwqo4yp
parent: robertc at robertcollins.net-20080815041247-xycggdgkzkkeide4
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2008-08-15 06:40:08 +0100
message:
(robertc) Improve bzr rm to detect missing files and have an alias to
del. (Robert Collins)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/blackbox/test_remove.py test_remove.py-20060530011439-fika5rm84lon0goe-1
------------------------------------------------------------
revno: 3619.5.4
revision-id: robertc at robertcollins.net-20080815041247-xycggdgkzkkeide4
parent: robertc at robertcollins.net-20080815024542-leg2xenbvo883tcc
parent: pqm at pqm.ubuntu.com-20080815025810-eguaiqf0kwwqo4yp
committer: Robert Collins <robertc at robertcollins.net>
branch nick: integration
timestamp: Fri 2008-08-15 14:12:47 +1000
message:
Resolve NEWS conflicts.
added:
doc/en/developer-guide/testing.txt testing.txt-20080812140359-i70zzh6v2z7grqex-1
modified:
Makefile Makefile-20050805140406-d96e3498bb61c5bb
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/__init__.py __init__.py-20050309040759-33e65acf91bbcd5d
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/diff.py diff.py-20050309040759-26944fbbf2ebbf36
bzrlib/help_topics/en/rules.txt rules.txt-20080516063844-ghr5l6pvvrhiycun-1
bzrlib/index.py index.py-20070712131115-lolkarso50vjr64s-1
bzrlib/osutils.py osutils.py-20050309040759-eeaff12fbf77ac86
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/repofmt/pack_repo.py pack_repo.py-20070813041115-gjv5ma7ktfqwsjgn-1
bzrlib/repository.py rev_storage.py-20051111201905-119e9401e46257e3
bzrlib/smart/medium.py medium.py-20061103051856-rgu2huy59fkz902q-1
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_init.py test_init.py-20060309032856-a292116204d86eb7
bzrlib/tests/blackbox/test_shared_repository.py test_shared_repository.py-20060317053531-ed30c0d79325e483
bzrlib/tests/commands/test_init.py test_init.py-20070514074921-audbcdd8o56dpame-1
bzrlib/tests/commands/test_init_repository.py test_init_repository-20070525163812-87xw0678ky573l27-1
bzrlib/tests/http_utils.py HTTPTestUtil.py-20050914180604-247d3aafb7a43343
bzrlib/tests/interrepository_implementations/test_fetch.py test_fetch.py-20080425213627-j60cjh782ufm83ry-1
bzrlib/tests/intertree_implementations/test_compare.py test_compare.py-20060724101752-09ysswo1a92uqyoz-2
bzrlib/tests/test_bzrdir.py test_bzrdir.py-20060131065654-deba40eef51cf220
bzrlib/tests/test_config.py testconfig.py-20051011041908-742d0c15d8d8c8eb
bzrlib/tests/test_http.py testhttp.py-20051018020158-b2eef6e867c514d9
bzrlib/tests/test_plugins.py plugins.py-20050622075746-32002b55e5e943e9
bzrlib/tests/test_source.py test_source.py-20051207061333-a58dea6abecc030d
bzrlib/tests/test_transform.py test_transaction.py-20060105172520-b3ffb3946550e6c4
bzrlib/tests/tree_implementations/test_iter_search_rules.py test_iter_search_rul-20080528065532-1ml1ttb12az20cxf-1
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
bzrlib/transport/http/__init__.py http_transport.py-20050711212304-506c5fd1059ace96
bzrlib/transport/local.py local_transport.py-20050711165921-9b1f142bfe480c24
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
doc/developers/HACKING.txt HACKING-20050805200004-2a5dc975d870f78c
doc/en/user-guide/configuring_bazaar.txt configuring_bazaar.t-20071128000722-ncxiua259xwbdbg7-1
setup.py setup.py-20050314065409-02f8a0a6e3f9bc70
tools/win32/bzr.iss.cog bzr.iss.cog-20060622100836-b3yup582rt3y0nvm-5
------------------------------------------------------------
revno: 3619.5.3
revision-id: robertc at robertcollins.net-20080815024542-leg2xenbvo883tcc
parent: robertc at robertcollins.net-20080813040249-dijrxmf7fzsolt6p
committer: Robert Collins <robertc at robertcollins.net>
branch nick: rm
timestamp: Fri 2008-08-15 12:45:42 +1000
message:
Review feedback.
modified:
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/blackbox/test_remove.py test_remove.py-20060530011439-fika5rm84lon0goe-1
------------------------------------------------------------
revno: 3619.5.2
revision-id: robertc at robertcollins.net-20080813040249-dijrxmf7fzsolt6p
parent: robertc at robertcollins.net-20080813035223-i9sxgq2rp5477oc5
committer: Robert Collins <robertc at robertcollins.net>
branch nick: rm
timestamp: Wed 2008-08-13 14:02:49 +1000
message:
* ``bzr rm`` is now aliased to ``bzr del`` for the convenience of svn
users. (Robert Collins, #205416)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
------------------------------------------------------------
revno: 3619.5.1
revision-id: robertc at robertcollins.net-20080813035223-i9sxgq2rp5477oc5
parent: robertc at robertcollins.net-20080813032119-09pl9q0t9gxng5t6
committer: Robert Collins <robertc at robertcollins.net>
branch nick: rm
timestamp: Wed 2008-08-13 13:52:23 +1000
message:
* ``bzr rm`` will now scan for files that are missing and remove just
them automatically, much as ``bzr add`` scans for new files that
are not ignored and adds them automatically. (Robert Collins)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/blackbox/test_remove.py test_remove.py-20060530011439-fika5rm84lon0goe-1
=== modified file 'NEWS'
--- a/NEWS 2008-08-14 20:42:22 +0000
+++ b/NEWS 2008-08-15 04:12:47 +0000
@@ -12,6 +12,10 @@
* ``bzr export`` can now export a subdirectory of a project.
(Robert Collins)
+ * ``bzr rm`` will now scan for files that are missing and remove just
+ them automatically, much as ``bzr add`` scans for new files that
+ are not ignored and adds them automatically. (Robert Collins)
+
IMPROVEMENTS:
* ``bzr init`` and ``bzr init-repo`` will now print out the same as
@@ -20,6 +24,9 @@
BUG FIXES:
+ * ``bzr rm`` is now aliased to ``bzr del`` for the convenience of svn
+ users. (Robert Collins, #205416)
+
* ``WorkingTree4`` trees will now correctly report missing-and-new
paths in the output of ``iter_changes``. (Robert Collins)
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2008-08-14 17:08:56 +0000
+++ b/bzrlib/builtins.py 2008-08-15 04:12:47 +0000
@@ -1066,17 +1066,14 @@
class cmd_remove(Command):
"""Remove files or directories.
- 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
- in the specified directories will be removed. If the directories are
- also new, they will also be removed.
+ This makes bzr stop tracking changes to the specified files. bzr will delete
+ them if they can easily be recovered using revert. If no options or
+ parameters are given bzr will scan for files that are being tracked by bzr
+ but missing in your tree and stop tracking them for you.
"""
takes_args = ['file*']
takes_options = ['verbose',
- Option('new', help='Remove newly-added files.'),
+ Option('new', help='Only remove files that have never been committed.'),
RegistryOption.from_kwargs('file-deletion-strategy',
'The file deletion mode to be used.',
title='Deletion Strategy', value_switches=True, enum_switch=False,
@@ -1085,7 +1082,7 @@
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']
+ aliases = ['rm', 'del']
encoding_type = 'replace'
def run(self, file_list, verbose=False, new=False,
@@ -1094,19 +1091,32 @@
if file_list is not None:
file_list = [f for f in file_list]
- 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,
- keep_files=file_deletion_strategy=='keep',
- force=file_deletion_strategy=='force')
+ tree.lock_write()
+ try:
+ # Heuristics should probably all move into tree.remove_smart or
+ # some such?
+ 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.')
+ elif file_list is None:
+ # missing files show up in iter_changes(basis) as
+ # versioned-with-no-kind.
+ missing = []
+ for change in tree.iter_changes(tree.basis_tree()):
+ # Find paths in the working tree that have no kind:
+ if change[1][1] is not None and change[6][1] is None:
+ missing.append(change[1][1])
+ file_list = sorted(missing, reverse=True)
+ file_deletion_strategy = 'keep'
+ tree.remove(file_list, verbose=verbose, to_file=self.outf,
+ keep_files=file_deletion_strategy=='keep',
+ force=file_deletion_strategy=='force')
+ finally:
+ tree.unlock()
class cmd_file_id(Command):
=== modified file 'bzrlib/tests/blackbox/test_remove.py'
--- a/bzrlib/tests/blackbox/test_remove.py 2007-11-28 17:02:05 +0000
+++ b/bzrlib/tests/blackbox/test_remove.py 2008-08-15 02:45:42 +0000
@@ -18,7 +18,7 @@
import os
import sys
-from bzrlib.tests import TestSkipped
+from bzrlib.tests import SymlinkFeature, TestSkipped
from bzrlib.tests.blackbox import ExternalBase
from bzrlib.workingtree import WorkingTree
from bzrlib import osutils
@@ -33,15 +33,16 @@
class TestRemove(ExternalBase):
- def _make_add_and_assert_tree(self, files):
+ def _make_tree_and_add(self, paths):
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)
+ tree.lock_write()
+ try:
+ self.build_tree(paths)
+ for path in paths:
+ file_id=str(path).replace('/', '_') + _id
+ tree.add(path, file_id)
+ finally:
+ tree.unlock()
return tree
def assertFilesDeleted(self, files):
@@ -70,35 +71,95 @@
#see if we can force it now
self.run_bzr(['remove', '--force'] + list(files_to_remove))
+ def test_remove_new_no_files_specified(self):
+ tree = self.make_branch_and_tree('.')
+ self.run_bzr_error(["bzr: ERROR: No matching files."], 'remove --new')
+ self.run_bzr_error(["bzr: ERROR: No matching files."], 'remove --new .')
+
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 .')
+ tree = self._make_tree_and_add(['foo'])
+ out, err = self.run_bzr(['rm'])
+ self.assertEqual('', err)
+ self.assertEqual('', out)
+ self.assertInWorkingTree('foo', tree=tree)
+ self.failUnlessExists('foo')
+
+ def test_remove_no_files_specified_missing_dir_and_contents(self):
+ tree = self._make_tree_and_add(
+ ['foo', 'dir/', 'dir/missing/', 'dir/missing/child'])
+ self.get_transport('.').delete_tree('dir/missing')
+ out, err = self.run_bzr(['rm'])
+ self.assertEqual('', out)
+ self.assertEqual(
+ 'removed dir/missing/child\n'
+ 'removed dir/missing\n',
+ err)
+ # non-missing paths not touched:
+ self.assertInWorkingTree('foo', tree=tree)
+ self.failUnlessExists('foo')
+ self.assertInWorkingTree('dir', tree=tree)
+ self.failUnlessExists('dir')
+ # missing files unversioned
+ self.assertNotInWorkingTree('dir/missing', tree=tree)
+ self.assertNotInWorkingTree('dir/missing/child', tree=tree)
+
+ def test_remove_no_files_specified_already_deleted(self):
+ tree = self._make_tree_and_add(['foo', 'bar'])
+ tree.commit('save foo and bar')
+ os.unlink('bar')
+ self.run_bzr(['rm'])
+ self.assertEqual(None, tree.path2id('bar'))
+ # Running rm with a deleted file does not error.
+ out, err = self.run_bzr(['rm'])
+ self.assertEqual('', out)
+ self.assertEqual('', err)
+
+ def test_remove_no_files_specified_missing_file(self):
+ tree = self._make_tree_and_add(['foo', 'bar'])
+ os.unlink('bar')
+ out, err = self.run_bzr(['rm'])
+ self.assertEqual('', out)
+ self.assertEqual('removed bar\n', err)
+ # non-missing files not touched:
+ self.assertInWorkingTree('foo', tree=tree)
+ self.failUnlessExists('foo')
+ # missing files unversioned
+ self.assertNotInWorkingTree('bar', tree=tree)
+
+ def test_remove_no_files_specified_missing_link(self):
+ self.requireFeature(SymlinkFeature)
+ tree = self._make_tree_and_add(['foo'])
+ os.symlink('foo', 'linkname')
+ tree.add(['linkname'])
+ os.unlink('linkname')
+ out, err = self.run_bzr(['rm'])
+ self.assertEqual('', out)
+ self.assertEqual('removed linkname\n', err)
+ # non-missing files not touched:
+ self.assertInWorkingTree('foo', tree=tree)
+ self.failUnlessExists('foo')
+ # missing files unversioned
+ self.assertNotInWorkingTree('linkname', tree=tree)
def test_rm_one_file(self):
- tree = self._make_add_and_assert_tree([a])
+ tree = self._make_tree_and_add([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])
+ tree = self._make_tree_and_add([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])
+ tree = self._make_tree_and_add([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])
+ tree = self._make_tree_and_add([a])
self.run_bzr("commit -m 'added a'")
os.unlink(a)
self.assertInWorkingTree(a)
@@ -117,21 +178,21 @@
['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)
+ tree = self._make_tree_and_add(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_ignored_files(self):
- tree = self._make_add_and_assert_tree(['a'])
+ tree = self._make_tree_and_add(['a'])
self.run_bzr(['ignore', 'a'])
self.run_bzr_remove_changed_files(['added:[.\s]*a'], ['a'])
def test_remove_changed_files_from_child_dir(self):
if sys.platform == 'win32':
raise TestSkipped("Windows unable to remove '.' directory")
- tree = self._make_add_and_assert_tree(files)
+ tree = self._make_tree_and_add(files)
self.run_bzr("commit -m 'added files'")
self.changeFile(a)
self.changeFile(c)
@@ -157,7 +218,7 @@
self.assertFilesDeleted(files)
def test_remove_deleted_files(self):
- tree = self._make_add_and_assert_tree(files)
+ tree = self._make_tree_and_add(files)
self.run_bzr("commit -m 'added files'")
my_files=[f for f in files]
my_files.sort(reverse=True)
@@ -170,15 +231,15 @@
self.failIfExists(files)
def test_remove_non_existing_files(self):
- tree = self._make_add_and_assert_tree([])
+ tree = self._make_tree_and_add([])
self.run_bzr(['remove', 'b'])
def test_remove_keep_non_existing_files(self):
- tree = self._make_add_and_assert_tree([])
+ tree = self._make_tree_and_add([])
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)
+ tree = self._make_tree_and_add(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",
@@ -186,7 +247,7 @@
self.assertFilesDeleted(files)
def test_remove_keep_files(self):
- tree = self._make_add_and_assert_tree(files)
+ tree = self._make_tree_and_add(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",
@@ -194,13 +255,13 @@
self.assertFilesUnversioned(files)
def test_remove_with_new(self):
- tree = self._make_add_and_assert_tree(files)
+ tree = self._make_tree_and_add(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)
+ tree = self._make_tree_and_add(files)
self.run_bzr('remove --new --keep b b/c',
error_regexes=["removed b", "removed b/c"])
tree = WorkingTree.open('.')
@@ -209,7 +270,7 @@
self.assertFilesUnversioned([b,c])
def test_remove_with_new_in_dir2(self):
- tree = self._make_add_and_assert_tree(files)
+ tree = self._make_tree_and_add(files)
self.run_bzr('remove --new --keep .',
error_regexes=["removed a", "removed b", "removed b/c"])
tree = WorkingTree.open('.')
More information about the bazaar-commits
mailing list