1
# Copyright (C) 2006 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
28
from bzrlib.bzrdir import BzrDir
29
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
30
UnversionedParent, ParentLoop, DeletingParent,)
31
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
32
ReusingTransform, CantMoveRoot,
33
PathsNotVersionedError, ExistingLimbo,
34
ImmortalLimbo, LockError)
35
from bzrlib.osutils import file_kind, has_symlinks, pathjoin
36
from bzrlib.merge import Merge3Merger
37
from bzrlib.tests import TestCaseInTempDir, TestSkipped, TestCase
38
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
39
resolve_conflicts, cook_conflicts,
40
find_interesting, build_tree, get_backup_name)
43
class TestTreeTransform(tests.TestCaseWithTransport):
46
super(TestTreeTransform, self).setUp()
47
self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
50
def get_transform(self):
51
transform = TreeTransform(self.wt)
52
#self.addCleanup(transform.finalize)
53
return transform, transform.root
55
def test_existing_limbo(self):
56
limbo_name = urlutils.local_path_from_url(
57
self.wt._control_files.controlfilename('limbo'))
58
transform, root = self.get_transform()
59
os.mkdir(pathjoin(limbo_name, 'hehe'))
60
self.assertRaises(ImmortalLimbo, transform.apply)
61
self.assertRaises(LockError, self.wt.unlock)
62
self.assertRaises(ExistingLimbo, self.get_transform)
63
self.assertRaises(LockError, self.wt.unlock)
64
os.rmdir(pathjoin(limbo_name, 'hehe'))
66
transform, root = self.get_transform()
70
transform, root = self.get_transform()
71
self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
72
imaginary_id = transform.trans_id_tree_path('imaginary')
73
imaginary_id2 = transform.trans_id_tree_path('imaginary/')
74
self.assertEqual(imaginary_id, imaginary_id2)
75
self.assertEqual(transform.get_tree_parent(imaginary_id), root)
76
self.assertEqual(transform.final_kind(root), 'directory')
77
self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
78
trans_id = transform.create_path('name', root)
79
self.assertIs(transform.final_file_id(trans_id), None)
80
self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
81
transform.create_file('contents', trans_id)
82
transform.set_executability(True, trans_id)
83
transform.version_file('my_pretties', trans_id)
84
self.assertRaises(DuplicateKey, transform.version_file,
85
'my_pretties', trans_id)
86
self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
87
self.assertEqual(transform.final_parent(trans_id), root)
88
self.assertIs(transform.final_parent(root), ROOT_PARENT)
89
self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
90
oz_id = transform.create_path('oz', root)
91
transform.create_directory(oz_id)
92
transform.version_file('ozzie', oz_id)
93
trans_id2 = transform.create_path('name2', root)
94
transform.create_file('contents', trans_id2)
95
transform.set_executability(False, trans_id2)
96
transform.version_file('my_pretties2', trans_id2)
97
modified_paths = transform.apply().modified_paths
98
self.assertEqual('contents', self.wt.get_file_byname('name').read())
99
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
100
self.assertIs(self.wt.is_executable('my_pretties'), True)
101
self.assertIs(self.wt.is_executable('my_pretties2'), False)
102
self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
103
self.assertEqual(len(modified_paths), 3)
104
tree_mod_paths = [self.wt.id2abspath(f) for f in
105
('ozzie', 'my_pretties', 'my_pretties2')]
106
self.assertSubset(tree_mod_paths, modified_paths)
107
# is it safe to finalize repeatedly?
111
def test_convenience(self):
112
transform, root = self.get_transform()
113
trans_id = transform.new_file('name', root, 'contents',
115
oz = transform.new_directory('oz', root, 'oz-id')
116
dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
117
toto = transform.new_file('toto', dorothy, 'toto-contents',
120
self.assertEqual(len(transform.find_conflicts()), 0)
122
self.assertRaises(ReusingTransform, transform.find_conflicts)
123
self.assertEqual('contents', file(self.wt.abspath('name')).read())
124
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
125
self.assertIs(self.wt.is_executable('my_pretties'), True)
126
self.assertEqual(self.wt.path2id('oz'), 'oz-id')
127
self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
128
self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
130
self.assertEqual('toto-contents',
131
self.wt.get_file_byname('oz/dorothy/toto').read())
132
self.assertIs(self.wt.is_executable('toto-id'), False)
134
def test_tree_reference(self):
135
transform, root = self.get_transform()
136
tree = transform._tree
137
trans_id = transform.new_directory('reference', root, 'subtree-id')
138
transform.set_tree_reference('subtree-revision', trans_id)
141
self.addCleanup(tree.unlock)
142
self.assertEqual('subtree-revision',
143
tree.inventory['subtree-id'].reference_revision)
145
def test_conflicts(self):
146
transform, root = self.get_transform()
147
trans_id = transform.new_file('name', root, 'contents',
149
self.assertEqual(len(transform.find_conflicts()), 0)
150
trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
151
self.assertEqual(transform.find_conflicts(),
152
[('duplicate', trans_id, trans_id2, 'name')])
153
self.assertRaises(MalformedTransform, transform.apply)
154
transform.adjust_path('name', trans_id, trans_id2)
155
self.assertEqual(transform.find_conflicts(),
156
[('non-directory parent', trans_id)])
157
tinman_id = transform.trans_id_tree_path('tinman')
158
transform.adjust_path('name', tinman_id, trans_id2)
159
self.assertEqual(transform.find_conflicts(),
160
[('unversioned parent', tinman_id),
161
('missing parent', tinman_id)])
162
lion_id = transform.create_path('lion', root)
163
self.assertEqual(transform.find_conflicts(),
164
[('unversioned parent', tinman_id),
165
('missing parent', tinman_id)])
166
transform.adjust_path('name', lion_id, trans_id2)
167
self.assertEqual(transform.find_conflicts(),
168
[('unversioned parent', lion_id),
169
('missing parent', lion_id)])
170
transform.version_file("Courage", lion_id)
171
self.assertEqual(transform.find_conflicts(),
172
[('missing parent', lion_id),
173
('versioning no contents', lion_id)])
174
transform.adjust_path('name2', root, trans_id2)
175
self.assertEqual(transform.find_conflicts(),
176
[('versioning no contents', lion_id)])
177
transform.create_file('Contents, okay?', lion_id)
178
transform.adjust_path('name2', trans_id2, trans_id2)
179
self.assertEqual(transform.find_conflicts(),
180
[('parent loop', trans_id2),
181
('non-directory parent', trans_id2)])
182
transform.adjust_path('name2', root, trans_id2)
183
oz_id = transform.new_directory('oz', root)
184
transform.set_executability(True, oz_id)
185
self.assertEqual(transform.find_conflicts(),
186
[('unversioned executability', oz_id)])
187
transform.version_file('oz-id', oz_id)
188
self.assertEqual(transform.find_conflicts(),
189
[('non-file executability', oz_id)])
190
transform.set_executability(None, oz_id)
191
tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
193
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
194
self.assertEqual('contents', file(self.wt.abspath('name')).read())
195
transform2, root = self.get_transform()
196
oz_id = transform2.trans_id_tree_file_id('oz-id')
197
newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
198
result = transform2.find_conflicts()
199
fp = FinalPaths(transform2)
200
self.assert_('oz/tip' in transform2._tree_path_ids)
201
self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
202
self.assertEqual(len(result), 2)
203
self.assertEqual((result[0][0], result[0][1]),
204
('duplicate', newtip))
205
self.assertEqual((result[1][0], result[1][2]),
206
('duplicate id', newtip))
207
transform2.finalize()
208
transform3 = TreeTransform(self.wt)
209
self.addCleanup(transform3.finalize)
210
oz_id = transform3.trans_id_tree_file_id('oz-id')
211
transform3.delete_contents(oz_id)
212
self.assertEqual(transform3.find_conflicts(),
213
[('missing parent', oz_id)])
214
root_id = transform3.root
215
tip_id = transform3.trans_id_tree_file_id('tip-id')
216
transform3.adjust_path('tip', root_id, tip_id)
219
def test_add_del(self):
220
start, root = self.get_transform()
221
start.new_directory('a', root, 'a')
223
transform, root = self.get_transform()
224
transform.delete_versioned(transform.trans_id_tree_file_id('a'))
225
transform.new_directory('a', root, 'a')
228
def test_unversioning(self):
229
create_tree, root = self.get_transform()
230
parent_id = create_tree.new_directory('parent', root, 'parent-id')
231
create_tree.new_file('child', parent_id, 'child', 'child-id')
233
unversion = TreeTransform(self.wt)
234
self.addCleanup(unversion.finalize)
235
parent = unversion.trans_id_tree_path('parent')
236
unversion.unversion_file(parent)
237
self.assertEqual(unversion.find_conflicts(),
238
[('unversioned parent', parent_id)])
239
file_id = unversion.trans_id_tree_file_id('child-id')
240
unversion.unversion_file(file_id)
243
def test_name_invariants(self):
244
create_tree, root = self.get_transform()
246
root = create_tree.root
247
create_tree.new_file('name1', root, 'hello1', 'name1')
248
create_tree.new_file('name2', root, 'hello2', 'name2')
249
ddir = create_tree.new_directory('dying_directory', root, 'ddir')
250
create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
251
create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
252
create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
255
mangle_tree,root = self.get_transform()
256
root = mangle_tree.root
258
name1 = mangle_tree.trans_id_tree_file_id('name1')
259
name2 = mangle_tree.trans_id_tree_file_id('name2')
260
mangle_tree.adjust_path('name2', root, name1)
261
mangle_tree.adjust_path('name1', root, name2)
263
#tests for deleting parent directories
264
ddir = mangle_tree.trans_id_tree_file_id('ddir')
265
mangle_tree.delete_contents(ddir)
266
dfile = mangle_tree.trans_id_tree_file_id('dfile')
267
mangle_tree.delete_versioned(dfile)
268
mangle_tree.unversion_file(dfile)
269
mfile = mangle_tree.trans_id_tree_file_id('mfile')
270
mangle_tree.adjust_path('mfile', root, mfile)
272
#tests for adding parent directories
273
newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
274
mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
275
mangle_tree.adjust_path('mfile2', newdir, mfile2)
276
mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
277
self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
278
self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
279
self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
281
self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
282
self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
283
mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
284
self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
285
self.assertEqual(file(mfile2_path).read(), 'later2')
286
self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
287
self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
288
newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
289
self.assertEqual(file(newfile_path).read(), 'hello3')
290
self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
291
self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
292
mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
294
def test_both_rename(self):
295
create_tree,root = self.get_transform()
296
newdir = create_tree.new_directory('selftest', root, 'selftest-id')
297
create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
299
mangle_tree,root = self.get_transform()
300
selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
301
blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
302
mangle_tree.adjust_path('test', root, selftest)
303
mangle_tree.adjust_path('test_too_much', root, selftest)
304
mangle_tree.set_executability(True, blackbox)
307
def test_both_rename2(self):
308
create_tree,root = self.get_transform()
309
bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
310
tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
311
blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
312
create_tree.new_file('test_too_much.py', blackbox, 'hello1',
315
mangle_tree,root = self.get_transform()
316
bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
317
tests = mangle_tree.trans_id_tree_file_id('tests-id')
318
test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
319
mangle_tree.adjust_path('selftest', bzrlib, tests)
320
mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
321
mangle_tree.set_executability(True, test_too_much)
324
def test_both_rename3(self):
325
create_tree,root = self.get_transform()
326
tests = create_tree.new_directory('tests', root, 'tests-id')
327
create_tree.new_file('test_too_much.py', tests, 'hello1',
330
mangle_tree,root = self.get_transform()
331
tests = mangle_tree.trans_id_tree_file_id('tests-id')
332
test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
333
mangle_tree.adjust_path('selftest', root, tests)
334
mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
335
mangle_tree.set_executability(True, test_too_much)
338
def test_move_dangling_ie(self):
339
create_tree, root = self.get_transform()
341
root = create_tree.root
342
create_tree.new_file('name1', root, 'hello1', 'name1')
344
delete_contents, root = self.get_transform()
345
file = delete_contents.trans_id_tree_file_id('name1')
346
delete_contents.delete_contents(file)
347
delete_contents.apply()
348
move_id, root = self.get_transform()
349
name1 = move_id.trans_id_tree_file_id('name1')
350
newdir = move_id.new_directory('dir', root, 'newdir')
351
move_id.adjust_path('name2', newdir, name1)
354
def test_replace_dangling_ie(self):
355
create_tree, root = self.get_transform()
357
root = create_tree.root
358
create_tree.new_file('name1', root, 'hello1', 'name1')
360
delete_contents = TreeTransform(self.wt)
361
self.addCleanup(delete_contents.finalize)
362
file = delete_contents.trans_id_tree_file_id('name1')
363
delete_contents.delete_contents(file)
364
delete_contents.apply()
365
delete_contents.finalize()
366
replace = TreeTransform(self.wt)
367
self.addCleanup(replace.finalize)
368
name2 = replace.new_file('name2', root, 'hello2', 'name1')
369
conflicts = replace.find_conflicts()
370
name1 = replace.trans_id_tree_file_id('name1')
371
self.assertEqual(conflicts, [('duplicate id', name1, name2)])
372
resolve_conflicts(replace)
375
def test_symlinks(self):
376
if not has_symlinks():
377
raise TestSkipped('Symlinks are not supported on this platform')
378
transform,root = self.get_transform()
379
oz_id = transform.new_directory('oz', root, 'oz-id')
380
wizard = transform.new_symlink('wizard', oz_id, 'wizard-target',
382
wiz_id = transform.create_path('wizard2', oz_id)
383
transform.create_symlink('behind_curtain', wiz_id)
384
transform.version_file('wiz-id2', wiz_id)
385
transform.set_executability(True, wiz_id)
386
self.assertEqual(transform.find_conflicts(),
387
[('non-file executability', wiz_id)])
388
transform.set_executability(None, wiz_id)
390
self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
391
self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
392
self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')),
394
self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
398
def get_conflicted(self):
399
create,root = self.get_transform()
400
create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
401
oz = create.new_directory('oz', root, 'oz-id')
402
create.new_directory('emeraldcity', oz, 'emerald-id')
404
conflicts,root = self.get_transform()
405
# set up duplicate entry, duplicate id
406
new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
408
old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
409
oz = conflicts.trans_id_tree_file_id('oz-id')
410
# set up DeletedParent parent conflict
411
conflicts.delete_versioned(oz)
412
emerald = conflicts.trans_id_tree_file_id('emerald-id')
413
# set up MissingParent conflict
414
munchkincity = conflicts.trans_id_file_id('munchkincity-id')
415
conflicts.adjust_path('munchkincity', root, munchkincity)
416
conflicts.new_directory('auntem', munchkincity, 'auntem-id')
418
conflicts.adjust_path('emeraldcity', emerald, emerald)
419
return conflicts, emerald, oz, old_dorothy, new_dorothy
421
def test_conflict_resolution(self):
422
conflicts, emerald, oz, old_dorothy, new_dorothy =\
423
self.get_conflicted()
424
resolve_conflicts(conflicts)
425
self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
426
self.assertIs(conflicts.final_file_id(old_dorothy), None)
427
self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
428
self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
429
self.assertEqual(conflicts.final_parent(emerald), oz)
432
def test_cook_conflicts(self):
433
tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
434
raw_conflicts = resolve_conflicts(tt)
435
cooked_conflicts = cook_conflicts(raw_conflicts, tt)
436
duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
437
'dorothy', None, 'dorothy-id')
438
self.assertEqual(cooked_conflicts[0], duplicate)
439
duplicate_id = DuplicateID('Unversioned existing file',
440
'dorothy.moved', 'dorothy', None,
442
self.assertEqual(cooked_conflicts[1], duplicate_id)
443
missing_parent = MissingParent('Created directory', 'munchkincity',
445
deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
446
self.assertEqual(cooked_conflicts[2], missing_parent)
447
unversioned_parent = UnversionedParent('Versioned directory',
450
unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
452
self.assertEqual(cooked_conflicts[3], unversioned_parent)
453
parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
454
'oz/emeraldcity', 'emerald-id', 'emerald-id')
455
self.assertEqual(cooked_conflicts[4], deleted_parent)
456
self.assertEqual(cooked_conflicts[5], unversioned_parent2)
457
self.assertEqual(cooked_conflicts[6], parent_loop)
458
self.assertEqual(len(cooked_conflicts), 7)
461
def test_string_conflicts(self):
462
tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
463
raw_conflicts = resolve_conflicts(tt)
464
cooked_conflicts = cook_conflicts(raw_conflicts, tt)
466
conflicts_s = [str(c) for c in cooked_conflicts]
467
self.assertEqual(len(cooked_conflicts), len(conflicts_s))
468
self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy. '
469
'Moved existing file to '
471
self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy. '
472
'Unversioned existing file '
474
self.assertEqual(conflicts_s[2], 'Conflict adding files to'
475
' munchkincity. Created directory.')
476
self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
477
' versioned, but has versioned'
478
' children. Versioned directory.')
479
self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
480
" is not empty. Not deleting.")
481
self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
482
' versioned, but has versioned'
483
' children. Versioned directory.')
484
self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
485
' oz/emeraldcity. Cancelled move.')
487
def test_moving_versioned_directories(self):
488
create, root = self.get_transform()
489
kansas = create.new_directory('kansas', root, 'kansas-id')
490
create.new_directory('house', kansas, 'house-id')
491
create.new_directory('oz', root, 'oz-id')
493
cyclone, root = self.get_transform()
494
oz = cyclone.trans_id_tree_file_id('oz-id')
495
house = cyclone.trans_id_tree_file_id('house-id')
496
cyclone.adjust_path('house', oz, house)
499
def test_moving_root(self):
500
create, root = self.get_transform()
501
fun = create.new_directory('fun', root, 'fun-id')
502
create.new_directory('sun', root, 'sun-id')
503
create.new_directory('moon', root, 'moon')
505
transform, root = self.get_transform()
506
transform.adjust_root_path('oldroot', fun)
507
new_root=transform.trans_id_tree_path('')
508
transform.version_file('new-root', new_root)
511
def test_renames(self):
512
create, root = self.get_transform()
513
old = create.new_directory('old-parent', root, 'old-id')
514
intermediate = create.new_directory('intermediate', old, 'im-id')
515
myfile = create.new_file('myfile', intermediate, 'myfile-text',
518
rename, root = self.get_transform()
519
old = rename.trans_id_file_id('old-id')
520
rename.adjust_path('new', root, old)
521
myfile = rename.trans_id_file_id('myfile-id')
522
rename.set_executability(True, myfile)
525
def test_find_interesting(self):
526
create, root = self.get_transform()
528
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
529
create.new_file('uvfile', root, 'othertext')
531
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
532
find_interesting, wt, wt, ['vfile'])
533
self.assertEqual(result, set(['myfile-id']))
535
def test_set_executability_order(self):
536
"""Ensure that executability behaves the same, no matter what order.
538
- create file and set executability simultaneously
539
- create file and set executability afterward
540
- unsetting the executability of a file whose executability has not been
541
declared should throw an exception (this may happen when a
542
merge attempts to create a file with a duplicate ID)
544
transform, root = self.get_transform()
546
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
548
sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
549
transform.set_executability(True, sac)
550
uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
551
self.assertRaises(KeyError, transform.set_executability, None, uws)
553
self.assertTrue(wt.is_executable('soc'))
554
self.assertTrue(wt.is_executable('sac'))
556
def test_preserve_mode(self):
557
"""File mode is preserved when replacing content"""
558
if sys.platform == 'win32':
559
raise TestSkipped('chmod has no effect on win32')
560
transform, root = self.get_transform()
561
transform.new_file('file1', root, 'contents', 'file1-id', True)
563
self.assertTrue(self.wt.is_executable('file1-id'))
564
transform, root = self.get_transform()
565
file1_id = transform.trans_id_tree_file_id('file1-id')
566
transform.delete_contents(file1_id)
567
transform.create_file('contents2', file1_id)
569
self.assertTrue(self.wt.is_executable('file1-id'))
571
def test__set_mode_stats_correctly(self):
572
"""_set_mode stats to determine file mode."""
573
if sys.platform == 'win32':
574
raise TestSkipped('chmod has no effect on win32')
578
def instrumented_stat(path):
579
stat_paths.append(path)
580
return real_stat(path)
582
transform, root = self.get_transform()
584
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
585
file_id='bar-id-1', executable=False)
588
transform, root = self.get_transform()
589
bar1_id = transform.trans_id_tree_path('bar')
590
bar2_id = transform.trans_id_tree_path('bar2')
592
os.stat = instrumented_stat
593
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
598
bar1_abspath = self.wt.abspath('bar')
599
self.assertEqual([bar1_abspath], stat_paths)
601
def test_iter_changes(self):
602
self.wt.set_root_id('eert_toor')
603
transform, root = self.get_transform()
604
transform.new_file('old', root, 'blah', 'id-1', True)
606
transform, root = self.get_transform()
608
self.assertEqual([], list(transform._iter_changes()))
609
old = transform.trans_id_tree_file_id('id-1')
610
transform.unversion_file(old)
611
self.assertEqual([('id-1', ('old', None), False, (True, False),
612
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
613
(True, True))], list(transform._iter_changes()))
614
transform.new_directory('new', root, 'id-1')
615
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
616
('eert_toor', 'eert_toor'), ('old', 'new'),
617
('file', 'directory'),
618
(True, False))], list(transform._iter_changes()))
622
def test_iter_changes_new(self):
623
self.wt.set_root_id('eert_toor')
624
transform, root = self.get_transform()
625
transform.new_file('old', root, 'blah')
627
transform, root = self.get_transform()
629
old = transform.trans_id_tree_path('old')
630
transform.version_file('id-1', old)
631
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
632
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
633
(False, False))], list(transform._iter_changes()))
637
def test_iter_changes_modifications(self):
638
self.wt.set_root_id('eert_toor')
639
transform, root = self.get_transform()
640
transform.new_file('old', root, 'blah', 'id-1')
641
transform.new_file('new', root, 'blah')
642
transform.new_directory('subdir', root, 'subdir-id')
644
transform, root = self.get_transform()
646
old = transform.trans_id_tree_path('old')
647
subdir = transform.trans_id_tree_file_id('subdir-id')
648
new = transform.trans_id_tree_path('new')
649
self.assertEqual([], list(transform._iter_changes()))
652
transform.delete_contents(old)
653
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
654
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
655
(False, False))], list(transform._iter_changes()))
658
transform.create_file('blah', old)
659
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
660
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
661
(False, False))], list(transform._iter_changes()))
662
transform.cancel_deletion(old)
663
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
664
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
665
(False, False))], list(transform._iter_changes()))
666
transform.cancel_creation(old)
668
# move file_id to a different file
669
self.assertEqual([], list(transform._iter_changes()))
670
transform.unversion_file(old)
671
transform.version_file('id-1', new)
672
transform.adjust_path('old', root, new)
673
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
674
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
675
(False, False))], list(transform._iter_changes()))
676
transform.cancel_versioning(new)
677
transform._removed_id = set()
680
self.assertEqual([], list(transform._iter_changes()))
681
transform.set_executability(True, old)
682
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
683
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
684
(False, True))], list(transform._iter_changes()))
685
transform.set_executability(None, old)
688
self.assertEqual([], list(transform._iter_changes()))
689
transform.adjust_path('new', root, old)
690
transform._new_parent = {}
691
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
692
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
693
(False, False))], list(transform._iter_changes()))
694
transform._new_name = {}
697
self.assertEqual([], list(transform._iter_changes()))
698
transform.adjust_path('new', subdir, old)
699
transform._new_name = {}
700
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
701
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
702
('file', 'file'), (False, False))],
703
list(transform._iter_changes()))
704
transform._new_path = {}
709
def test_iter_changes_modified_bleed(self):
710
self.wt.set_root_id('eert_toor')
711
"""Modified flag should not bleed from one change to another"""
712
# unfortunately, we have no guarantee that file1 (which is modified)
713
# will be applied before file2. And if it's applied after file2, it
714
# obviously can't bleed into file2's change output. But for now, it
716
transform, root = self.get_transform()
717
transform.new_file('file1', root, 'blah', 'id-1')
718
transform.new_file('file2', root, 'blah', 'id-2')
720
transform, root = self.get_transform()
722
transform.delete_contents(transform.trans_id_file_id('id-1'))
723
transform.set_executability(True,
724
transform.trans_id_file_id('id-2'))
725
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
726
('eert_toor', 'eert_toor'), ('file1', u'file1'),
727
('file', None), (False, False)),
728
('id-2', (u'file2', u'file2'), False, (True, True),
729
('eert_toor', 'eert_toor'), ('file2', u'file2'),
730
('file', 'file'), (False, True))],
731
list(transform._iter_changes()))
735
def test_iter_changes_move_missing(self):
736
"""Test moving ids with no files around"""
737
self.wt.set_root_id('toor_eert')
738
# Need two steps because versioning a non-existant file is a conflict.
739
transform, root = self.get_transform()
740
transform.new_directory('floater', root, 'floater-id')
742
transform, root = self.get_transform()
743
transform.delete_contents(transform.trans_id_tree_path('floater'))
745
transform, root = self.get_transform()
746
floater = transform.trans_id_tree_path('floater')
748
transform.adjust_path('flitter', root, floater)
749
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
750
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
751
(None, None), (False, False))], list(transform._iter_changes()))
755
def test_iter_changes_pointless(self):
756
"""Ensure that no-ops are not treated as modifications"""
757
self.wt.set_root_id('eert_toor')
758
transform, root = self.get_transform()
759
transform.new_file('old', root, 'blah', 'id-1')
760
transform.new_directory('subdir', root, 'subdir-id')
762
transform, root = self.get_transform()
764
old = transform.trans_id_tree_path('old')
765
subdir = transform.trans_id_tree_file_id('subdir-id')
766
self.assertEqual([], list(transform._iter_changes()))
767
transform.delete_contents(subdir)
768
transform.create_directory(subdir)
769
transform.set_executability(False, old)
770
transform.unversion_file(old)
771
transform.version_file('id-1', old)
772
transform.adjust_path('old', root, old)
773
self.assertEqual([], list(transform._iter_changes()))
777
class TransformGroup(object):
778
def __init__(self, dirname, root_id):
781
self.wt = BzrDir.create_standalone_workingtree(dirname)
782
self.wt.set_root_id(root_id)
783
self.b = self.wt.branch
784
self.tt = TreeTransform(self.wt)
785
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
788
def conflict_text(tree, merge):
789
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
790
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
793
class TestTransformMerge(TestCaseInTempDir):
794
def test_text_merge(self):
795
root_id = generate_ids.gen_root_id()
796
base = TransformGroup("base", root_id)
797
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
798
base.tt.new_file('b', base.root, 'b1', 'b')
799
base.tt.new_file('c', base.root, 'c', 'c')
800
base.tt.new_file('d', base.root, 'd', 'd')
801
base.tt.new_file('e', base.root, 'e', 'e')
802
base.tt.new_file('f', base.root, 'f', 'f')
803
base.tt.new_directory('g', base.root, 'g')
804
base.tt.new_directory('h', base.root, 'h')
806
other = TransformGroup("other", root_id)
807
other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
808
other.tt.new_file('b', other.root, 'b2', 'b')
809
other.tt.new_file('c', other.root, 'c2', 'c')
810
other.tt.new_file('d', other.root, 'd', 'd')
811
other.tt.new_file('e', other.root, 'e2', 'e')
812
other.tt.new_file('f', other.root, 'f', 'f')
813
other.tt.new_file('g', other.root, 'g', 'g')
814
other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
815
other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
817
this = TransformGroup("this", root_id)
818
this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
819
this.tt.new_file('b', this.root, 'b', 'b')
820
this.tt.new_file('c', this.root, 'c', 'c')
821
this.tt.new_file('d', this.root, 'd2', 'd')
822
this.tt.new_file('e', this.root, 'e2', 'e')
823
this.tt.new_file('f', this.root, 'f', 'f')
824
this.tt.new_file('g', this.root, 'g', 'g')
825
this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
826
this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
828
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
830
self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
831
# three-way text conflict
832
self.assertEqual(this.wt.get_file('b').read(),
833
conflict_text('b', 'b2'))
835
self.assertEqual(this.wt.get_file('c').read(), 'c2')
837
self.assertEqual(this.wt.get_file('d').read(), 'd2')
838
# Ambigious clean merge
839
self.assertEqual(this.wt.get_file('e').read(), 'e2')
841
self.assertEqual(this.wt.get_file('f').read(), 'f')
842
# Correct correct results when THIS == OTHER
843
self.assertEqual(this.wt.get_file('g').read(), 'g')
844
# Text conflict when THIS & OTHER are text and BASE is dir
845
self.assertEqual(this.wt.get_file('h').read(),
846
conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
847
self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
849
self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
851
self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
852
self.assertEqual(this.wt.get_file('i').read(),
853
conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
854
self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
856
self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
858
self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
859
modified = ['a', 'b', 'c', 'h', 'i']
860
merge_modified = this.wt.merge_modified()
861
self.assertSubset(merge_modified, modified)
862
self.assertEqual(len(merge_modified), len(modified))
863
file(this.wt.id2abspath('a'), 'wb').write('booga')
865
merge_modified = this.wt.merge_modified()
866
self.assertSubset(merge_modified, modified)
867
self.assertEqual(len(merge_modified), len(modified))
871
def test_file_merge(self):
872
if not has_symlinks():
873
raise TestSkipped('Symlinks are not supported on this platform')
874
root_id = generate_ids.gen_root_id()
875
base = TransformGroup("BASE", root_id)
876
this = TransformGroup("THIS", root_id)
877
other = TransformGroup("OTHER", root_id)
878
for tg in this, base, other:
879
tg.tt.new_directory('a', tg.root, 'a')
880
tg.tt.new_symlink('b', tg.root, 'b', 'b')
881
tg.tt.new_file('c', tg.root, 'c', 'c')
882
tg.tt.new_symlink('d', tg.root, tg.name, 'd')
883
targets = ((base, 'base-e', 'base-f', None, None),
884
(this, 'other-e', 'this-f', 'other-g', 'this-h'),
885
(other, 'other-e', None, 'other-g', 'other-h'))
886
for tg, e_target, f_target, g_target, h_target in targets:
887
for link, target in (('e', e_target), ('f', f_target),
888
('g', g_target), ('h', h_target)):
889
if target is not None:
890
tg.tt.new_symlink(link, tg.root, target, link)
892
for tg in this, base, other:
894
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
895
self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
896
self.assertIs(os.path.islink(this.wt.abspath('b')), True)
897
self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
898
for suffix in ('THIS', 'BASE', 'OTHER'):
899
self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
900
self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
901
self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
902
self.assertEqual(this.wt.id2path('f'), 'f.THIS')
903
self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
904
self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
905
self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
906
self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
907
self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
908
self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
909
self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
910
self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
911
self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
912
self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
914
def test_filename_merge(self):
915
root_id = generate_ids.gen_root_id()
916
base = TransformGroup("BASE", root_id)
917
this = TransformGroup("THIS", root_id)
918
other = TransformGroup("OTHER", root_id)
919
base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
920
for t in [base, this, other]]
921
base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
922
for t in [base, this, other]]
923
base.tt.new_directory('c', base_a, 'c')
924
this.tt.new_directory('c1', this_a, 'c')
925
other.tt.new_directory('c', other_b, 'c')
927
base.tt.new_directory('d', base_a, 'd')
928
this.tt.new_directory('d1', this_b, 'd')
929
other.tt.new_directory('d', other_a, 'd')
931
base.tt.new_directory('e', base_a, 'e')
932
this.tt.new_directory('e', this_a, 'e')
933
other.tt.new_directory('e1', other_b, 'e')
935
base.tt.new_directory('f', base_a, 'f')
936
this.tt.new_directory('f1', this_b, 'f')
937
other.tt.new_directory('f1', other_b, 'f')
939
for tg in [this, base, other]:
941
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
942
self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
943
self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
944
self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
945
self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
947
def test_filename_merge_conflicts(self):
948
root_id = generate_ids.gen_root_id()
949
base = TransformGroup("BASE", root_id)
950
this = TransformGroup("THIS", root_id)
951
other = TransformGroup("OTHER", root_id)
952
base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
953
for t in [base, this, other]]
954
base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
955
for t in [base, this, other]]
957
base.tt.new_file('g', base_a, 'g', 'g')
958
other.tt.new_file('g1', other_b, 'g1', 'g')
960
base.tt.new_file('h', base_a, 'h', 'h')
961
this.tt.new_file('h1', this_b, 'h1', 'h')
963
base.tt.new_file('i', base.root, 'i', 'i')
964
other.tt.new_directory('i1', this_b, 'i')
966
for tg in [this, base, other]:
968
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
970
self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
971
self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
972
self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
973
self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
974
self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
975
self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
976
self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
979
class TestBuildTree(tests.TestCaseWithTransport):
981
def test_build_tree(self):
982
if not has_symlinks():
983
raise TestSkipped('Test requires symlink support')
985
a = BzrDir.create_standalone_workingtree('a')
987
file('a/foo/bar', 'wb').write('contents')
988
os.symlink('a/foo/bar', 'a/foo/baz')
989
a.add(['foo', 'foo/bar', 'foo/baz'])
990
a.commit('initial commit')
991
b = BzrDir.create_standalone_workingtree('b')
992
basis = a.basis_tree()
994
self.addCleanup(basis.unlock)
996
self.assertIs(os.path.isdir('b/foo'), True)
997
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
998
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1000
def test_build_with_references(self):
1001
tree = self.make_branch_and_tree('source',
1002
format='dirstate-with-subtree')
1003
subtree = self.make_branch_and_tree('source/subtree',
1004
format='dirstate-with-subtree')
1005
tree.add_reference(subtree)
1006
tree.commit('a revision')
1007
tree.branch.create_checkout('target')
1008
self.failUnlessExists('target')
1009
self.failUnlessExists('target/subtree')
1011
def test_file_conflict_handling(self):
1012
"""Ensure that when building trees, conflict handling is done"""
1013
source = self.make_branch_and_tree('source')
1014
target = self.make_branch_and_tree('target')
1015
self.build_tree(['source/file', 'target/file'])
1016
source.add('file', 'new-file')
1017
source.commit('added file')
1018
build_tree(source.basis_tree(), target)
1019
self.assertEqual([DuplicateEntry('Moved existing file to',
1020
'file.moved', 'file', None, 'new-file')],
1022
target2 = self.make_branch_and_tree('target2')
1023
target_file = file('target2/file', 'wb')
1025
source_file = file('source/file', 'rb')
1027
target_file.write(source_file.read())
1032
build_tree(source.basis_tree(), target2)
1033
self.assertEqual([], target2.conflicts())
1035
def test_symlink_conflict_handling(self):
1036
"""Ensure that when building trees, conflict handling is done"""
1037
if not has_symlinks():
1038
raise TestSkipped('Test requires symlink support')
1039
source = self.make_branch_and_tree('source')
1040
os.symlink('foo', 'source/symlink')
1041
source.add('symlink', 'new-symlink')
1042
source.commit('added file')
1043
target = self.make_branch_and_tree('target')
1044
os.symlink('bar', 'target/symlink')
1045
build_tree(source.basis_tree(), target)
1046
self.assertEqual([DuplicateEntry('Moved existing file to',
1047
'symlink.moved', 'symlink', None, 'new-symlink')],
1049
target = self.make_branch_and_tree('target2')
1050
os.symlink('foo', 'target2/symlink')
1051
build_tree(source.basis_tree(), target)
1052
self.assertEqual([], target.conflicts())
1054
def test_directory_conflict_handling(self):
1055
"""Ensure that when building trees, conflict handling is done"""
1056
source = self.make_branch_and_tree('source')
1057
target = self.make_branch_and_tree('target')
1058
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1059
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1060
source.commit('added file')
1061
build_tree(source.basis_tree(), target)
1062
self.assertEqual([], target.conflicts())
1063
self.failUnlessExists('target/dir1/file')
1065
# Ensure contents are merged
1066
target = self.make_branch_and_tree('target2')
1067
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1068
build_tree(source.basis_tree(), target)
1069
self.assertEqual([], target.conflicts())
1070
self.failUnlessExists('target2/dir1/file2')
1071
self.failUnlessExists('target2/dir1/file')
1073
# Ensure new contents are suppressed for existing branches
1074
target = self.make_branch_and_tree('target3')
1075
self.make_branch('target3/dir1')
1076
self.build_tree(['target3/dir1/file2'])
1077
build_tree(source.basis_tree(), target)
1078
self.failIfExists('target3/dir1/file')
1079
self.failUnlessExists('target3/dir1/file2')
1080
self.failUnlessExists('target3/dir1.diverted/file')
1081
self.assertEqual([DuplicateEntry('Diverted to',
1082
'dir1.diverted', 'dir1', 'new-dir1', None)],
1085
target = self.make_branch_and_tree('target4')
1086
self.build_tree(['target4/dir1/'])
1087
self.make_branch('target4/dir1/file')
1088
build_tree(source.basis_tree(), target)
1089
self.failUnlessExists('target4/dir1/file')
1090
self.assertEqual('directory', file_kind('target4/dir1/file'))
1091
self.failUnlessExists('target4/dir1/file.diverted')
1092
self.assertEqual([DuplicateEntry('Diverted to',
1093
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1096
def test_mixed_conflict_handling(self):
1097
"""Ensure that when building trees, conflict handling is done"""
1098
source = self.make_branch_and_tree('source')
1099
target = self.make_branch_and_tree('target')
1100
self.build_tree(['source/name', 'target/name/'])
1101
source.add('name', 'new-name')
1102
source.commit('added file')
1103
build_tree(source.basis_tree(), target)
1104
self.assertEqual([DuplicateEntry('Moved existing file to',
1105
'name.moved', 'name', None, 'new-name')], target.conflicts())
1107
def test_raises_in_populated(self):
1108
source = self.make_branch_and_tree('source')
1109
self.build_tree(['source/name'])
1111
source.commit('added name')
1112
target = self.make_branch_and_tree('target')
1113
self.build_tree(['target/name'])
1115
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1116
build_tree, source.basis_tree(), target)
1119
class MockTransform(object):
1121
def has_named_child(self, by_parent, parent_id, name):
1122
for child_id in by_parent[parent_id]:
1126
elif name == "name.~%s~" % child_id:
1130
class MockEntry(object):
1132
object.__init__(self)
1135
class TestGetBackupName(TestCase):
1136
def test_get_backup_name(self):
1137
tt = MockTransform()
1138
name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1139
self.assertEqual(name, 'name.~1~')
1140
name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
1141
self.assertEqual(name, 'name.~2~')
1142
name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1143
self.assertEqual(name, 'name.~1~')
1144
name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1145
self.assertEqual(name, 'name.~1~')
1146
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1147
self.assertEqual(name, 'name.~4~')