1
# Copyright (C) 2006-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Test for 'bzr mv'"""
27
from bzrlib.tests import (
28
TestCaseWithTransport,
30
from bzrlib.tests.features import (
31
CaseInsensitiveFilesystemFeature,
33
UnicodeFilenameFeature,
37
class TestMove(TestCaseWithTransport):
39
def assertMoved(self,from_path,to_path):
40
"""Assert that to_path is existing and versioned but from_path not. """
41
self.assertPathDoesNotExist(from_path)
42
self.assertNotInWorkingTree(from_path)
44
self.assertPathExists(to_path)
45
self.assertInWorkingTree(to_path)
47
def test_mv_modes(self):
48
"""Test two modes of operation for mv"""
49
tree = self.make_branch_and_tree('.')
50
files = self.build_tree(['a', 'c', 'subdir/'])
51
tree.add(['a', 'c', 'subdir'])
53
self.run_bzr('mv a b')
54
self.assertMoved('a','b')
56
self.run_bzr('mv b subdir')
57
self.assertMoved('b','subdir/b')
59
self.run_bzr('mv subdir/b a')
60
self.assertMoved('subdir/b','a')
62
self.run_bzr('mv a c subdir')
63
self.assertMoved('a','subdir/a')
64
self.assertMoved('c','subdir/c')
66
self.run_bzr('mv subdir/a subdir/newa')
67
self.assertMoved('subdir/a','subdir/newa')
69
def test_mv_unversioned(self):
70
self.build_tree(['unversioned.txt'])
72
["^bzr: ERROR: Could not rename unversioned.txt => elsewhere."
73
" .*unversioned.txt is not versioned\.$"],
74
'mv unversioned.txt elsewhere')
76
def test_mv_nonexisting(self):
78
["^bzr: ERROR: Could not rename doesnotexist => somewhereelse."
79
" .*doesnotexist is not versioned\.$"],
80
'mv doesnotexist somewhereelse')
82
def test_mv_unqualified(self):
83
self.run_bzr_error(['^bzr: ERROR: missing file argument$'], 'mv')
85
def test_mv_invalid(self):
86
tree = self.make_branch_and_tree('.')
87
self.build_tree(['test.txt', 'sub1/'])
88
tree.add(['test.txt'])
91
["^bzr: ERROR: Could not move to sub1: sub1 is not versioned\.$"],
95
["^bzr: ERROR: Could not move test.txt => .*hello.txt: "
96
"sub1 is not versioned\.$"],
97
'mv test.txt sub1/hello.txt')
99
def test_mv_dirs(self):
100
tree = self.make_branch_and_tree('.')
101
self.build_tree(['hello.txt', 'sub1/'])
102
tree.add(['hello.txt', 'sub1'])
104
self.run_bzr('mv sub1 sub2')
105
self.assertMoved('sub1','sub2')
107
self.run_bzr('mv hello.txt sub2')
108
self.assertMoved('hello.txt','sub2/hello.txt')
110
self.build_tree(['sub1/'])
112
self.run_bzr('mv sub2/hello.txt sub1')
113
self.assertMoved('sub2/hello.txt','sub1/hello.txt')
115
self.run_bzr('mv sub2 sub1')
116
self.assertMoved('sub2','sub1/sub2')
118
def test_mv_relative(self):
119
self.build_tree(['sub1/', 'sub1/sub2/', 'sub1/hello.txt'])
120
tree = self.make_branch_and_tree('.')
121
tree.add(['sub1', 'sub1/sub2', 'sub1/hello.txt'])
123
os.chdir('sub1/sub2')
124
self.run_bzr('mv ../hello.txt .')
125
self.assertPathExists('./hello.txt')
128
self.run_bzr('mv sub2/hello.txt .')
130
self.assertMoved('sub1/sub2/hello.txt','sub1/hello.txt')
132
def test_mv_change_case_file(self):
133
# test for bug #77740 (mv unable change filename case on Windows)
134
tree = self.make_branch_and_tree('.')
135
self.build_tree(['test.txt'])
136
tree.add(['test.txt'])
137
self.run_bzr('mv test.txt Test.txt')
138
# we can't use failUnlessExists on case-insensitive filesystem
139
# so try to check shape of the tree
140
shape = sorted(os.listdir(u'.'))
141
self.assertEqual(['.bzr', 'Test.txt'], shape)
142
self.assertInWorkingTree('Test.txt')
143
self.assertNotInWorkingTree('test.txt')
145
def test_mv_change_case_dir(self):
146
tree = self.make_branch_and_tree('.')
147
self.build_tree(['foo/'])
149
self.run_bzr('mv foo Foo')
150
# we can't use failUnlessExists on case-insensitive filesystem
151
# so try to check shape of the tree
152
shape = sorted(os.listdir(u'.'))
153
self.assertEqual(['.bzr', 'Foo'], shape)
154
self.assertInWorkingTree('Foo')
155
self.assertNotInWorkingTree('foo')
157
def test_mv_change_case_dir_w_files(self):
158
tree = self.make_branch_and_tree('.')
159
self.build_tree(['foo/', 'foo/bar'])
161
self.run_bzr('mv foo Foo')
162
# we can't use failUnlessExists on case-insensitive filesystem
163
# so try to check shape of the tree
164
shape = sorted(os.listdir(u'.'))
165
self.assertEqual(['.bzr', 'Foo'], shape)
166
self.assertInWorkingTree('Foo')
167
self.assertNotInWorkingTree('foo')
169
def test_mv_file_to_wrong_case_dir(self):
170
self.requireFeature(CaseInsensitiveFilesystemFeature)
171
tree = self.make_branch_and_tree('.')
172
self.build_tree(['foo/', 'bar'])
173
tree.add(['foo', 'bar'])
174
out, err = self.run_bzr('mv bar Foo', retcode=3)
175
self.assertEquals('', out)
177
'bzr: ERROR: Could not move to Foo: Foo is not versioned.\n',
180
def test_mv_smoke_aliases(self):
181
# just test that aliases for mv exist, if their behaviour is changed in
182
# the future, then extend the tests.
183
self.build_tree(['a'])
184
tree = self.make_branch_and_tree('.')
187
self.run_bzr('move a b')
188
self.run_bzr('rename b a')
190
def test_mv_through_symlinks(self):
191
self.requireFeature(SymlinkFeature)
192
tree = self.make_branch_and_tree('.')
193
self.build_tree(['a/', 'a/b'])
196
tree.add(['a', 'a/b', 'c'], ['a-id', 'b-id', 'c-id'])
197
self.run_bzr('mv c/b b')
198
tree = workingtree.WorkingTree.open('.')
199
self.assertEqual('b-id', tree.path2id('b'))
201
def test_mv_already_moved_file(self):
202
"""Test bzr mv original_file to moved_file.
204
Tests if a file which has allready been moved by an external tool,
205
is handled correctly by bzr mv.
206
Setup: a is in the working tree, b does not exist.
207
User does: mv a b; bzr mv a b
209
self.build_tree(['a'])
210
tree = self.make_branch_and_tree('.')
213
osutils.rename('a', 'b')
214
self.run_bzr('mv a b')
215
self.assertMoved('a','b')
217
def test_mv_already_moved_file_to_versioned_target(self):
218
"""Test bzr mv existing_file to versioned_file.
220
Tests if an attempt to move an existing versioned file
221
to another versiond file will fail.
222
Setup: a and b are in the working tree.
223
User does: rm b; mv a b; bzr mv a b
225
self.build_tree(['a', 'b'])
226
tree = self.make_branch_and_tree('.')
230
osutils.rename('a', 'b')
232
["^bzr: ERROR: Could not move a => b. b is already versioned\.$"],
234
#check that nothing changed
235
self.assertPathDoesNotExist('a')
236
self.assertPathExists('b')
238
def test_mv_already_moved_file_into_subdir(self):
239
"""Test bzr mv original_file to versioned_directory/file.
241
Tests if a file which has already been moved into a versioned
242
directory by an external tool, is handled correctly by bzr mv.
243
Setup: a and sub/ are in the working tree.
244
User does: mv a sub/a; bzr mv a sub/a
246
self.build_tree(['a', 'sub/'])
247
tree = self.make_branch_and_tree('.')
248
tree.add(['a', 'sub'])
250
osutils.rename('a', 'sub/a')
251
self.run_bzr('mv a sub/a')
252
self.assertMoved('a','sub/a')
254
def test_mv_already_moved_file_into_unversioned_subdir(self):
255
"""Test bzr mv original_file to unversioned_directory/file.
257
Tests if an attempt to move an existing versioned file
258
into an unversioned directory will fail.
259
Setup: a is in the working tree, sub/ is not.
260
User does: mv a sub/a; bzr mv a sub/a
262
self.build_tree(['a', 'sub/'])
263
tree = self.make_branch_and_tree('.')
266
osutils.rename('a', 'sub/a')
268
["^bzr: ERROR: Could not move a => a: sub is not versioned\.$"],
270
self.assertPathDoesNotExist('a')
271
self.assertPathExists('sub/a')
273
def test_mv_already_moved_files_into_subdir(self):
274
"""Test bzr mv original_files to versioned_directory.
276
Tests if files which has already been moved into a versioned
277
directory by an external tool, is handled correctly by bzr mv.
278
Setup: a1, a2, sub are in the working tree.
279
User does: mv a1 sub/.; bzr mv a1 a2 sub
281
self.build_tree(['a1', 'a2', 'sub/'])
282
tree = self.make_branch_and_tree('.')
283
tree.add(['a1', 'a2', 'sub'])
285
osutils.rename('a1', 'sub/a1')
286
self.run_bzr('mv a1 a2 sub')
287
self.assertMoved('a1','sub/a1')
288
self.assertMoved('a2','sub/a2')
290
def test_mv_already_moved_files_into_unversioned_subdir(self):
291
"""Test bzr mv original_file to unversioned_directory.
293
Tests if an attempt to move existing versioned file
294
into an unversioned directory will fail.
295
Setup: a1, a2 are in the working tree, sub is not.
296
User does: mv a1 sub/.; bzr mv a1 a2 sub
298
self.build_tree(['a1', 'a2', 'sub/'])
299
tree = self.make_branch_and_tree('.')
300
tree.add(['a1', 'a2'])
302
osutils.rename('a1', 'sub/a1')
304
["^bzr: ERROR: Could not move to sub. sub is not versioned\.$"],
306
self.assertPathDoesNotExist('a1')
307
self.assertPathExists('sub/a1')
308
self.assertPathExists('a2')
309
self.assertPathDoesNotExist('sub/a2')
311
def test_mv_already_moved_file_forcing_after(self):
312
"""Test bzr mv versioned_file to unversioned_file.
314
Tests if an attempt to move an existing versioned file to an existing
315
unversioned file will fail, informing the user to use the --after
316
option to force this.
317
Setup: a is in the working tree, b not versioned.
318
User does: mv a b; touch a; bzr mv a b
320
self.build_tree(['a', 'b'])
321
tree = self.make_branch_and_tree('.')
324
osutils.rename('a', 'b')
325
self.build_tree(['a']) #touch a
327
["^bzr: ERROR: Could not rename a => b because both files exist."
328
" \(Use --after to tell bzr about a rename that has already"
331
self.assertPathExists('a')
332
self.assertPathExists('b')
334
def test_mv_already_moved_file_using_after(self):
335
"""Test bzr mv --after versioned_file to unversioned_file.
337
Tests if an existing versioned file can be forced to move to an
338
existing unversioned file using the --after option. With the result
339
that bazaar considers the unversioned_file to be moved from
340
versioned_file and versioned_file will become unversioned.
341
Setup: a is in the working tree and b exists.
342
User does: mv a b; touch a; bzr mv a b --after
343
Resulting in a => b and a is unknown.
345
self.build_tree(['a', 'b'])
346
tree = self.make_branch_and_tree('.')
348
osutils.rename('a', 'b')
349
self.build_tree(['a']) #touch a
351
self.run_bzr('mv a b --after')
352
self.assertPathExists('a')
353
self.assertNotInWorkingTree('a')#a should be unknown now.
354
self.assertPathExists('b')
355
self.assertInWorkingTree('b')
357
def test_mv_already_moved_files_forcing_after(self):
358
"""Test bzr mv versioned_files to directory/unversioned_file.
360
Tests if an attempt to move an existing versioned file to an existing
361
unversioned file in some other directory will fail, informing the user
362
to use the --after option to force this.
364
Setup: a1, a2, sub are versioned and in the working tree,
365
sub/a1, sub/a2 are in working tree.
366
User does: mv a* sub; touch a1; touch a2; bzr mv a1 a2 sub
368
self.build_tree(['a1', 'a2', 'sub/', 'sub/a1', 'sub/a2'])
369
tree = self.make_branch_and_tree('.')
370
tree.add(['a1', 'a2', 'sub'])
371
osutils.rename('a1', 'sub/a1')
372
osutils.rename('a2', 'sub/a2')
373
self.build_tree(['a1']) #touch a1
374
self.build_tree(['a2']) #touch a2
377
["^bzr: ERROR: Could not rename a1 => sub/a1 because both files"
378
" exist. \(Use --after to tell bzr about a rename that has already"
381
self.assertPathExists('a1')
382
self.assertPathExists('a2')
383
self.assertPathExists('sub/a1')
384
self.assertPathExists('sub/a2')
386
def test_mv_already_moved_files_using_after(self):
387
"""Test bzr mv --after versioned_file to directory/unversioned_file.
389
Tests if an existing versioned file can be forced to move to an
390
existing unversioned file in some other directory using the --after
391
option. With the result that bazaar considers
392
directory/unversioned_file to be moved from versioned_file and
393
versioned_file will become unversioned.
395
Setup: a1, a2, sub are versioned and in the working tree,
396
sub/a1, sub/a2 are in working tree.
397
User does: mv a* sub; touch a1; touch a2; bzr mv a1 a2 sub --after
399
self.build_tree(['a1', 'a2', 'sub/', 'sub/a1', 'sub/a2'])
400
tree = self.make_branch_and_tree('.')
401
tree.add(['a1', 'a2', 'sub'])
402
osutils.rename('a1', 'sub/a1')
403
osutils.rename('a2', 'sub/a2')
404
self.build_tree(['a1']) #touch a1
405
self.build_tree(['a2']) #touch a2
407
self.run_bzr('mv a1 a2 sub --after')
408
self.assertPathExists('a1')
409
self.assertPathExists('a2')
410
self.assertPathExists('sub/a1')
411
self.assertPathExists('sub/a2')
412
self.assertInWorkingTree('sub/a1')
413
self.assertInWorkingTree('sub/a2')
415
def test_mv_already_moved_directory(self):
416
"""Use `bzr mv a b` to mark a directory as renamed.
418
https://bugs.launchpad.net/bzr/+bug/107967/
420
self.build_tree(['a/', 'c/'])
421
tree = self.make_branch_and_tree('.')
423
osutils.rename('a', 'b')
424
osutils.rename('c', 'd')
425
# mv a b should work just like it does for already renamed files
426
self.run_bzr('mv a b')
427
self.assertPathDoesNotExist('a')
428
self.assertNotInWorkingTree('a')
429
self.assertPathExists('b')
430
self.assertInWorkingTree('b')
431
# and --after should work, too (technically it's ignored)
432
self.run_bzr('mv --after c d')
433
self.assertPathDoesNotExist('c')
434
self.assertNotInWorkingTree('c')
435
self.assertPathExists('d')
436
self.assertInWorkingTree('d')
438
def make_abcd_tree(self):
439
tree = self.make_branch_and_tree('tree')
440
self.build_tree(['tree/a', 'tree/c'])
442
tree.commit('record old names')
443
osutils.rename('tree/a', 'tree/b')
444
osutils.rename('tree/c', 'tree/d')
447
def test_mv_auto(self):
448
self.make_abcd_tree()
449
out, err = self.run_bzr('mv --auto', working_dir='tree')
450
self.assertEqual(out, '')
451
self.assertEqual(err, 'a => b\nc => d\n')
452
tree = workingtree.WorkingTree.open('tree')
453
self.assertIsNot(None, tree.path2id('b'))
454
self.assertIsNot(None, tree.path2id('d'))
456
def test_mv_auto_one_path(self):
457
self.make_abcd_tree()
458
out, err = self.run_bzr('mv --auto tree')
459
self.assertEqual(out, '')
460
self.assertEqual(err, 'a => b\nc => d\n')
461
tree = workingtree.WorkingTree.open('tree')
462
self.assertIsNot(None, tree.path2id('b'))
463
self.assertIsNot(None, tree.path2id('d'))
465
def test_mv_auto_two_paths(self):
466
self.make_abcd_tree()
467
out, err = self.run_bzr('mv --auto tree tree2', retcode=3)
468
self.assertEqual('bzr: ERROR: Only one path may be specified to'
471
def test_mv_auto_dry_run(self):
472
self.make_abcd_tree()
473
out, err = self.run_bzr('mv --auto --dry-run', working_dir='tree')
474
self.assertEqual(out, '')
475
self.assertEqual(err, 'a => b\nc => d\n')
476
tree = workingtree.WorkingTree.open('tree')
477
self.assertIsNot(None, tree.path2id('a'))
478
self.assertIsNot(None, tree.path2id('c'))
480
def test_mv_no_auto_dry_run(self):
481
self.make_abcd_tree()
482
out, err = self.run_bzr('mv c d --dry-run',
483
working_dir='tree', retcode=3)
484
self.assertEqual('bzr: ERROR: --dry-run requires --auto.\n', err)
486
def test_mv_auto_after(self):
487
self.make_abcd_tree()
488
out, err = self.run_bzr('mv --auto --after', working_dir='tree',
490
self.assertEqual('bzr: ERROR: --after cannot be specified with'
493
def test_mv_quiet(self):
494
tree = self.make_branch_and_tree('.')
495
self.build_tree(['aaa'])
497
out, err = self.run_bzr('mv --quiet aaa bbb')
498
self.assertEqual(out, '')
499
self.assertEqual(err, '')
501
def test_mv_readonly_lightweight_checkout(self):
502
branch = self.make_branch('foo')
503
branch = bzrlib.branch.Branch.open(self.get_readonly_url('foo'))
504
tree = branch.create_checkout('tree', lightweight=True)
505
self.build_tree(['tree/path'])
507
# If this fails, the tree is trying to acquire a branch lock, which it
509
self.run_bzr(['mv', 'tree/path', 'tree/path2'])
511
def test_mv_unversioned_non_ascii(self):
512
"""Clear error on mv of an unversioned non-ascii file, see lp:707954"""
513
self.requireFeature(UnicodeFilenameFeature)
514
tree = self.make_branch_and_tree(".")
515
self.build_tree([u"\xA7"])
516
out, err = self.run_bzr_error(["Could not rename", "not versioned"],
517
["mv", u"\xA7", "b"])