1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for interface conformance of 'WorkingTree.remove'"""
19
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
20
from bzrlib import errors, ignores, osutils
22
class TestRemove(TestCaseWithWorkingTree):
23
"""Tests WorkingTree.remove"""
25
files = ['a', 'b/', 'b/c', 'd/']
26
rfiles = ['b/c', 'b', 'a', 'd']
28
def get_tree(self, files):
29
tree = self.make_branch_and_tree('.')
30
self.build_tree(files)
31
self.failUnlessExists(files)
34
def get_committed_tree(self, files, message="Committing"):
35
tree = self.get_tree(files)
38
self.assertInWorkingTree(files)
41
def assertRemovedAndDeleted(self, files):
42
self.assertNotInWorkingTree(files)
43
self.failIfExists(files)
45
def assertRemovedAndNotDeleted(self, files):
46
self.assertNotInWorkingTree(files)
47
self.failUnlessExists(files)
49
def test_remove_keep(self):
50
"""Check that files and directories are unversioned but not deleted."""
51
tree = self.get_tree(TestRemove.files)
52
tree.add(TestRemove.files)
53
self.assertInWorkingTree(TestRemove.files)
55
tree.remove(TestRemove.files)
56
self.assertRemovedAndNotDeleted(TestRemove.files)
58
def test_remove_keep_subtree(self):
59
"""Check that a directory is unversioned but not deleted."""
60
tree = self.make_branch_and_tree('.')
61
subtree = self.make_branch_and_tree('subtree')
62
tree.add('subtree', 'subtree-id')
64
tree.remove('subtree')
65
self.assertRemovedAndNotDeleted('subtree')
67
def test_remove_unchanged_files(self):
68
"""Check that unchanged files are removed and deleted."""
69
tree = self.get_committed_tree(TestRemove.files)
70
tree.remove(TestRemove.files, keep_files=False)
71
self.assertRemovedAndDeleted(TestRemove.files)
74
def test_remove_added_files(self):
75
"""Removal of newly added files must fail."""
76
tree = self.get_tree(TestRemove.files)
77
tree.add(TestRemove.files)
78
self.assertInWorkingTree(TestRemove.files)
79
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
80
TestRemove.files, keep_files=False)
81
self.assertContainsRe(err.changes_as_text,
82
'(?s)added:.*a.*b/.*b/c.*d/')
83
self.assertInWorkingTree(TestRemove.files)
84
self.failUnlessExists(TestRemove.files)
87
def test_remove_changed_file(self):
88
"""Removal of a changed files must fail."""
89
tree = self.get_committed_tree(['a'])
90
self.build_tree_contents([('a', "some other new content!")])
91
self.assertInWorkingTree('a')
92
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
93
'a', keep_files=False)
94
self.assertContainsRe(err.changes_as_text, '(?s)modified:.*a')
95
self.assertInWorkingTree('a')
96
self.failUnlessExists('a')
99
def test_remove_deleted_files(self):
100
"""Check that files are removed if they don't exist any more."""
101
tree = self.get_committed_tree(TestRemove.files)
102
for f in TestRemove.rfiles:
103
osutils.delete_any(f)
104
self.assertInWorkingTree(TestRemove.files)
105
self.failIfExists(TestRemove.files)
106
tree.remove(TestRemove.files, keep_files=False)
107
self.assertRemovedAndDeleted(TestRemove.files)
110
def test_remove_renamed_files(self):
111
"""Check that files are removed even if they are renamed."""
112
tree = self.get_committed_tree(TestRemove.files)
114
for f in TestRemove.rfiles:
115
tree.rename_one(f,f+'x')
116
rfilesx = ['bx/cx', 'bx', 'ax', 'dx']
117
self.assertInWorkingTree(rfilesx)
118
self.failUnlessExists(rfilesx)
120
tree.remove(rfilesx, keep_files=False)
121
self.assertRemovedAndDeleted(rfilesx)
124
def test_remove_renamed_changed_files(self):
125
"""Check that files are not removed if they are renamed and changed."""
126
tree = self.get_committed_tree(TestRemove.files)
128
for f in TestRemove.rfiles:
129
tree.rename_one(f,f+'x')
130
rfilesx = ['bx/cx', 'bx', 'ax', 'dx']
131
self.build_tree_contents([('ax','changed and renamed!'),
132
('bx/cx','changed and renamed!')])
133
self.assertInWorkingTree(rfilesx)
134
self.failUnlessExists(rfilesx)
136
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
137
rfilesx, keep_files=False)
138
self.assertContainsRe(err.changes_as_text,
139
'(?s)modified:.*ax.*bx/cx')
140
self.assertInWorkingTree(rfilesx)
141
self.failUnlessExists(rfilesx)
144
def test_force_remove_changed_files(self):
145
"""Check that changed files are removed and deleted when forced."""
146
tree = self.get_tree(TestRemove.files)
147
tree.add(TestRemove.files)
148
self.assertInWorkingTree(TestRemove.files)
150
tree.remove(TestRemove.files, keep_files=False, force=True)
151
self.assertRemovedAndDeleted(TestRemove.files)
154
def test_remove_unknown_files(self):
155
"""Try to delete unknown files."""
156
tree = self.get_tree(TestRemove.files)
157
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
158
TestRemove.files, keep_files=False)
159
self.assertContainsRe(err.changes_as_text,
160
'(?s)unknown:.*d/.*b/c.*b/.*a.*')
163
def test_remove_nonexisting_files(self):
164
"""Try to delete non-existing files."""
165
tree = self.get_tree(TestRemove.files)
166
tree.remove([''], keep_files=False)
167
tree.remove(['xyz', 'abc/def'], keep_files=False)
170
def test_remove_unchanged_directory(self):
171
"""Unchanged directories should be deleted."""
172
files = ['b/', 'b/c', 'b/sub_directory/', 'b/sub_directory/with_file']
173
tree = self.get_committed_tree(files)
174
tree.remove('b', keep_files=False)
175
self.assertRemovedAndDeleted('b')
178
def test_remove_absent_directory(self):
179
"""Removing a absent directory succeeds without corruption (#150438)."""
180
paths = ['a/', 'a/b']
181
tree = self.get_committed_tree(paths)
182
self.get_transport('.').delete_tree('a')
184
self.assertRemovedAndDeleted('b')
187
def test_remove_unknown_ignored_files(self):
188
"""Unknown ignored files should be deleted."""
189
tree = self.get_committed_tree(['b/'])
190
ignores.add_runtime_ignores(["*ignored*"])
192
self.build_tree(['unknown_ignored_file'])
193
self.assertNotEquals(None, tree.is_ignored('unknown_ignored_file'))
194
tree.remove('unknown_ignored_file', keep_files=False)
195
self.assertRemovedAndDeleted('unknown_ignored_file')
197
self.build_tree(['b/unknown_ignored_file', 'b/unknown_ignored_dir/'])
198
self.assertNotEquals(None, tree.is_ignored('b/unknown_ignored_file'))
199
self.assertNotEquals(None, tree.is_ignored('b/unknown_ignored_dir'))
200
tree.remove('b', keep_files=False)
201
self.assertRemovedAndDeleted('b')
204
def test_remove_changed_ignored_files(self):
205
"""Changed ignored files should not be deleted."""
206
files = ['an_ignored_file']
207
tree = self.get_tree(files)
209
ignores.add_runtime_ignores(["*ignored*"])
210
self.assertInWorkingTree(files)
211
self.assertNotEquals(None, tree.is_ignored(files[0]))
212
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
213
files, keep_files=False)
214
self.assertContainsRe(err.changes_as_text,
215
'(?s)added:.*' + files[0])
216
self.assertInWorkingTree(files)
219
def test_dont_remove_directory_with_unknowns(self):
220
"""Directories with unknowns should not be deleted."""
221
directories = ['a/', 'b/', 'c/', 'c/c/']
222
tree = self.get_committed_tree(directories)
224
self.build_tree(['a/unknown_file'])
225
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
226
'a', keep_files=False)
227
self.assertContainsRe(err.changes_as_text,
228
'(?s)unknown:.*a/unknown_file')
230
self.build_tree(['b/unknown_directory'])
231
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
232
'b', keep_files=False)
233
self.assertContainsRe(err.changes_as_text,
234
'(?s)unknown:.*b/unknown_directory')
236
self.build_tree(['c/c/unknown_file'])
237
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
238
'c/c', keep_files=False)
239
self.assertContainsRe(err.changes_as_text,
240
'(?s)unknown:.*c/c/unknown_file')
242
self.assertInWorkingTree(directories)
243
self.failUnlessExists(directories)
246
def test_force_remove_directory_with_unknowns(self):
247
"""Unchanged non-empty directories should be deleted when forced."""
248
files = ['b/', 'b/c']
249
tree = self.get_committed_tree(files)
251
other_files = ['b/unknown_file', 'b/sub_directory/',
252
'b/sub_directory/with_file', 'b/sub_directory/sub_directory/']
253
self.build_tree(other_files)
255
self.assertInWorkingTree(files)
256
self.failUnlessExists(files)
258
tree.remove('b', keep_files=False, force=True)
260
self.assertRemovedAndDeleted(files)
261
self.assertRemovedAndDeleted(other_files)
264
def test_remove_directory_with_changed_file(self):
265
"""Refuse to delete directories with changed files."""
266
files = ['b/', 'b/c']
267
tree = self.get_committed_tree(files)
268
self.build_tree_contents([('b/c', "some other new content!")])
270
err = self.assertRaises(errors.BzrRemoveChangedFilesError, tree.remove,
271
'b', keep_files=False)
272
self.assertContainsRe(err.changes_as_text, '(?s)modified:.*b/c')
273
self.assertInWorkingTree(files)
274
self.failUnlessExists(files)
276
# see if we can force it now..
277
tree.remove('b', keep_files=False, force=True)
278
self.assertRemovedAndDeleted(files)
281
def test_remove_directory_with_renames(self):
282
"""Delete directory with renames in or out."""
284
files = ['a/', 'a/file', 'a/directory/', 'b/']
285
files_to_move = ['a/file', 'a/directory/']
287
tree = self.get_committed_tree(files)
288
# move stuff from a=>b
289
tree.move(['a/file', 'a/directory'], to_dir='b')
291
moved_files = ['b/file', 'b/directory/']
292
self.assertRemovedAndDeleted(files_to_move)
293
self.assertInWorkingTree(moved_files)
294
self.failUnlessExists(moved_files)
296
# check if it works with renames out
297
tree.remove('a', keep_files=False)
298
self.assertRemovedAndDeleted(['a/'])
300
# check if it works with renames in
301
tree.remove('b', keep_files=False)
302
self.assertRemovedAndDeleted(['b/'])
305
def test_non_cwd(self):
306
tree = self.make_branch_and_tree('tree')
307
self.build_tree(['tree/dir/', 'tree/dir/file'])
308
tree.add(['dir', 'dir/file'])
309
tree.commit('add file')
310
tree.remove('dir/', keep_files=False)
311
self.failIfExists('tree/dir/file')
312
self.assertNotInWorkingTree('tree/dir/file', 'tree')
315
def test_remove_uncommitted_removed_file(self):
317
tree = self.get_committed_tree(['a'])
318
tree.remove('a', keep_files=False)
319
tree.remove('a', keep_files=False)
320
self.failIfExists('a')
323
def test_remove_file_and_containing_dir(self):
324
tree = self.get_committed_tree(['config/', 'config/file'])
325
tree.remove('config/file', keep_files=False)
326
tree.remove('config', keep_files=False)
327
self.failIfExists('config/file')
328
self.failIfExists('config')
331
def test_remove_dir_before_bzr(self):
332
# As per bug #272648. Note that a file must be present in the directory
333
# or the bug doesn't manifest itself.
334
tree = self.get_committed_tree(['.aaa/', '.aaa/file'])
335
tree.remove('.aaa/', keep_files=False)
336
self.failIfExists('.aaa/file')
337
self.failIfExists('.aaa')