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
19
from StringIO import StringIO
27
revision as _mod_revision,
32
from bzrlib.bzrdir import BzrDir
33
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
34
UnversionedParent, ParentLoop, DeletingParent,
36
from bzrlib.diff import show_diff_trees
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
ReusingTransform, CantMoveRoot,
39
PathsNotVersionedError, ExistingLimbo,
40
ExistingPendingDeletion, ImmortalLimbo,
41
ImmortalPendingDeletion, LockError)
42
from bzrlib.osutils import file_kind, pathjoin
43
from bzrlib.merge import Merge3Merger
44
from bzrlib.tests import (
45
CaseInsensitiveFilesystemFeature,
52
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
53
resolve_conflicts, cook_conflicts,
54
build_tree, get_backup_name,
55
_FileMover, resolve_checkout,
58
class TestTreeTransform(tests.TestCaseWithTransport):
61
super(TestTreeTransform, self).setUp()
62
self.wt = self.make_branch_and_tree('.', format='dirstate-with-subtree')
65
def get_transform(self):
66
transform = TreeTransform(self.wt)
67
self.addCleanup(transform.finalize)
68
return transform, transform.root
70
def test_existing_limbo(self):
71
transform, root = self.get_transform()
72
limbo_name = transform._limbodir
73
deletion_path = transform._deletiondir
74
os.mkdir(pathjoin(limbo_name, 'hehe'))
75
self.assertRaises(ImmortalLimbo, transform.apply)
76
self.assertRaises(LockError, self.wt.unlock)
77
self.assertRaises(ExistingLimbo, self.get_transform)
78
self.assertRaises(LockError, self.wt.unlock)
79
os.rmdir(pathjoin(limbo_name, 'hehe'))
81
os.rmdir(deletion_path)
82
transform, root = self.get_transform()
85
def test_existing_pending_deletion(self):
86
transform, root = self.get_transform()
87
deletion_path = self._limbodir = urlutils.local_path_from_url(
88
transform._tree._transport.abspath('pending-deletion'))
89
os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
90
self.assertRaises(ImmortalPendingDeletion, transform.apply)
91
self.assertRaises(LockError, self.wt.unlock)
92
self.assertRaises(ExistingPendingDeletion, self.get_transform)
95
transform, root = self.get_transform()
96
self.wt.lock_tree_write()
97
self.addCleanup(self.wt.unlock)
98
self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
99
imaginary_id = transform.trans_id_tree_path('imaginary')
100
imaginary_id2 = transform.trans_id_tree_path('imaginary/')
101
self.assertEqual(imaginary_id, imaginary_id2)
102
self.assertEqual(transform.get_tree_parent(imaginary_id), root)
103
self.assertEqual(transform.final_kind(root), 'directory')
104
self.assertEqual(transform.final_file_id(root), self.wt.get_root_id())
105
trans_id = transform.create_path('name', root)
106
self.assertIs(transform.final_file_id(trans_id), None)
107
self.assertRaises(NoSuchFile, transform.final_kind, trans_id)
108
transform.create_file('contents', trans_id)
109
transform.set_executability(True, trans_id)
110
transform.version_file('my_pretties', trans_id)
111
self.assertRaises(DuplicateKey, transform.version_file,
112
'my_pretties', trans_id)
113
self.assertEqual(transform.final_file_id(trans_id), 'my_pretties')
114
self.assertEqual(transform.final_parent(trans_id), root)
115
self.assertIs(transform.final_parent(root), ROOT_PARENT)
116
self.assertIs(transform.get_tree_parent(root), ROOT_PARENT)
117
oz_id = transform.create_path('oz', root)
118
transform.create_directory(oz_id)
119
transform.version_file('ozzie', oz_id)
120
trans_id2 = transform.create_path('name2', root)
121
transform.create_file('contents', trans_id2)
122
transform.set_executability(False, trans_id2)
123
transform.version_file('my_pretties2', trans_id2)
124
modified_paths = transform.apply().modified_paths
125
self.assertEqual('contents', self.wt.get_file_byname('name').read())
126
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
127
self.assertIs(self.wt.is_executable('my_pretties'), True)
128
self.assertIs(self.wt.is_executable('my_pretties2'), False)
129
self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
130
self.assertEqual(len(modified_paths), 3)
131
tree_mod_paths = [self.wt.id2abspath(f) for f in
132
('ozzie', 'my_pretties', 'my_pretties2')]
133
self.assertSubset(tree_mod_paths, modified_paths)
134
# is it safe to finalize repeatedly?
138
def test_hardlink(self):
139
self.requireFeature(HardlinkFeature)
140
transform, root = self.get_transform()
141
transform.new_file('file1', root, 'contents')
143
target = self.make_branch_and_tree('target')
144
target_transform = TreeTransform(target)
145
trans_id = target_transform.create_path('file1', target_transform.root)
146
target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
147
target_transform.apply()
148
self.failUnlessExists('target/file1')
149
source_stat = os.stat(self.wt.abspath('file1'))
150
target_stat = os.stat('target/file1')
151
self.assertEqual(source_stat, target_stat)
153
def test_convenience(self):
154
transform, root = self.get_transform()
155
self.wt.lock_tree_write()
156
self.addCleanup(self.wt.unlock)
157
trans_id = transform.new_file('name', root, 'contents',
159
oz = transform.new_directory('oz', root, 'oz-id')
160
dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
161
toto = transform.new_file('toto', dorothy, 'toto-contents',
164
self.assertEqual(len(transform.find_conflicts()), 0)
166
self.assertRaises(ReusingTransform, transform.find_conflicts)
167
self.assertEqual('contents', file(self.wt.abspath('name')).read())
168
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
169
self.assertIs(self.wt.is_executable('my_pretties'), True)
170
self.assertEqual(self.wt.path2id('oz'), 'oz-id')
171
self.assertEqual(self.wt.path2id('oz/dorothy'), 'dorothy-id')
172
self.assertEqual(self.wt.path2id('oz/dorothy/toto'), 'toto-id')
174
self.assertEqual('toto-contents',
175
self.wt.get_file_byname('oz/dorothy/toto').read())
176
self.assertIs(self.wt.is_executable('toto-id'), False)
178
def test_tree_reference(self):
179
transform, root = self.get_transform()
180
tree = transform._tree
181
trans_id = transform.new_directory('reference', root, 'subtree-id')
182
transform.set_tree_reference('subtree-revision', trans_id)
185
self.addCleanup(tree.unlock)
186
self.assertEqual('subtree-revision',
187
tree.inventory['subtree-id'].reference_revision)
189
def test_conflicts(self):
190
transform, root = self.get_transform()
191
trans_id = transform.new_file('name', root, 'contents',
193
self.assertEqual(len(transform.find_conflicts()), 0)
194
trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
195
self.assertEqual(transform.find_conflicts(),
196
[('duplicate', trans_id, trans_id2, 'name')])
197
self.assertRaises(MalformedTransform, transform.apply)
198
transform.adjust_path('name', trans_id, trans_id2)
199
self.assertEqual(transform.find_conflicts(),
200
[('non-directory parent', trans_id)])
201
tinman_id = transform.trans_id_tree_path('tinman')
202
transform.adjust_path('name', tinman_id, trans_id2)
203
self.assertEqual(transform.find_conflicts(),
204
[('unversioned parent', tinman_id),
205
('missing parent', tinman_id)])
206
lion_id = transform.create_path('lion', root)
207
self.assertEqual(transform.find_conflicts(),
208
[('unversioned parent', tinman_id),
209
('missing parent', tinman_id)])
210
transform.adjust_path('name', lion_id, trans_id2)
211
self.assertEqual(transform.find_conflicts(),
212
[('unversioned parent', lion_id),
213
('missing parent', lion_id)])
214
transform.version_file("Courage", lion_id)
215
self.assertEqual(transform.find_conflicts(),
216
[('missing parent', lion_id),
217
('versioning no contents', lion_id)])
218
transform.adjust_path('name2', root, trans_id2)
219
self.assertEqual(transform.find_conflicts(),
220
[('versioning no contents', lion_id)])
221
transform.create_file('Contents, okay?', lion_id)
222
transform.adjust_path('name2', trans_id2, trans_id2)
223
self.assertEqual(transform.find_conflicts(),
224
[('parent loop', trans_id2),
225
('non-directory parent', trans_id2)])
226
transform.adjust_path('name2', root, trans_id2)
227
oz_id = transform.new_directory('oz', root)
228
transform.set_executability(True, oz_id)
229
self.assertEqual(transform.find_conflicts(),
230
[('unversioned executability', oz_id)])
231
transform.version_file('oz-id', oz_id)
232
self.assertEqual(transform.find_conflicts(),
233
[('non-file executability', oz_id)])
234
transform.set_executability(None, oz_id)
235
tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
237
self.assertEqual(self.wt.path2id('name'), 'my_pretties')
238
self.assertEqual('contents', file(self.wt.abspath('name')).read())
239
transform2, root = self.get_transform()
240
oz_id = transform2.trans_id_tree_file_id('oz-id')
241
newtip = transform2.new_file('tip', oz_id, 'other', 'tip-id')
242
result = transform2.find_conflicts()
243
fp = FinalPaths(transform2)
244
self.assert_('oz/tip' in transform2._tree_path_ids)
245
self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
246
self.assertEqual(len(result), 2)
247
self.assertEqual((result[0][0], result[0][1]),
248
('duplicate', newtip))
249
self.assertEqual((result[1][0], result[1][2]),
250
('duplicate id', newtip))
251
transform2.finalize()
252
transform3 = TreeTransform(self.wt)
253
self.addCleanup(transform3.finalize)
254
oz_id = transform3.trans_id_tree_file_id('oz-id')
255
transform3.delete_contents(oz_id)
256
self.assertEqual(transform3.find_conflicts(),
257
[('missing parent', oz_id)])
258
root_id = transform3.root
259
tip_id = transform3.trans_id_tree_file_id('tip-id')
260
transform3.adjust_path('tip', root_id, tip_id)
263
def test_conflict_on_case_insensitive(self):
264
tree = self.make_branch_and_tree('tree')
265
# Don't try this at home, kids!
266
# Force the tree to report that it is case sensitive, for conflict
268
tree.case_sensitive = True
269
transform = TreeTransform(tree)
270
self.addCleanup(transform.finalize)
271
transform.new_file('file', transform.root, 'content')
272
transform.new_file('FiLe', transform.root, 'content')
273
result = transform.find_conflicts()
274
self.assertEqual([], result)
276
# Force the tree to report that it is case insensitive, for conflict
278
tree.case_sensitive = False
279
transform = TreeTransform(tree)
280
self.addCleanup(transform.finalize)
281
transform.new_file('file', transform.root, 'content')
282
transform.new_file('FiLe', transform.root, 'content')
283
result = transform.find_conflicts()
284
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
286
def test_conflict_on_case_insensitive_existing(self):
287
tree = self.make_branch_and_tree('tree')
288
self.build_tree(['tree/FiLe'])
289
# Don't try this at home, kids!
290
# Force the tree to report that it is case sensitive, for conflict
292
tree.case_sensitive = True
293
transform = TreeTransform(tree)
294
self.addCleanup(transform.finalize)
295
transform.new_file('file', transform.root, 'content')
296
result = transform.find_conflicts()
297
self.assertEqual([], result)
299
# Force the tree to report that it is case insensitive, for conflict
301
tree.case_sensitive = False
302
transform = TreeTransform(tree)
303
self.addCleanup(transform.finalize)
304
transform.new_file('file', transform.root, 'content')
305
result = transform.find_conflicts()
306
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
308
def test_resolve_case_insensitive_conflict(self):
309
tree = self.make_branch_and_tree('tree')
310
# Don't try this at home, kids!
311
# Force the tree to report that it is case insensitive, for conflict
313
tree.case_sensitive = False
314
transform = TreeTransform(tree)
315
self.addCleanup(transform.finalize)
316
transform.new_file('file', transform.root, 'content')
317
transform.new_file('FiLe', transform.root, 'content')
318
resolve_conflicts(transform)
320
self.failUnlessExists('tree/file')
321
self.failUnlessExists('tree/FiLe.moved')
323
def test_resolve_checkout_case_conflict(self):
324
tree = self.make_branch_and_tree('tree')
325
# Don't try this at home, kids!
326
# Force the tree to report that it is case insensitive, for conflict
328
tree.case_sensitive = False
329
transform = TreeTransform(tree)
330
self.addCleanup(transform.finalize)
331
transform.new_file('file', transform.root, 'content')
332
transform.new_file('FiLe', transform.root, 'content')
333
resolve_conflicts(transform,
334
pass_func=lambda t, c: resolve_checkout(t, c, []))
336
self.failUnlessExists('tree/file')
337
self.failUnlessExists('tree/FiLe.moved')
339
def test_apply_case_conflict(self):
340
"""Ensure that a transform with case conflicts can always be applied"""
341
tree = self.make_branch_and_tree('tree')
342
transform = TreeTransform(tree)
343
self.addCleanup(transform.finalize)
344
transform.new_file('file', transform.root, 'content')
345
transform.new_file('FiLe', transform.root, 'content')
346
dir = transform.new_directory('dir', transform.root)
347
transform.new_file('dirfile', dir, 'content')
348
transform.new_file('dirFiLe', dir, 'content')
349
resolve_conflicts(transform)
351
self.failUnlessExists('tree/file')
352
if not os.path.exists('tree/FiLe.moved'):
353
self.failUnlessExists('tree/FiLe')
354
self.failUnlessExists('tree/dir/dirfile')
355
if not os.path.exists('tree/dir/dirFiLe.moved'):
356
self.failUnlessExists('tree/dir/dirFiLe')
358
def test_case_insensitive_limbo(self):
359
tree = self.make_branch_and_tree('tree')
360
# Don't try this at home, kids!
361
# Force the tree to report that it is case insensitive
362
tree.case_sensitive = False
363
transform = TreeTransform(tree)
364
self.addCleanup(transform.finalize)
365
dir = transform.new_directory('dir', transform.root)
366
first = transform.new_file('file', dir, 'content')
367
second = transform.new_file('FiLe', dir, 'content')
368
self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
369
self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
371
def test_add_del(self):
372
start, root = self.get_transform()
373
start.new_directory('a', root, 'a')
375
transform, root = self.get_transform()
376
transform.delete_versioned(transform.trans_id_tree_file_id('a'))
377
transform.new_directory('a', root, 'a')
380
def test_unversioning(self):
381
create_tree, root = self.get_transform()
382
parent_id = create_tree.new_directory('parent', root, 'parent-id')
383
create_tree.new_file('child', parent_id, 'child', 'child-id')
385
unversion = TreeTransform(self.wt)
386
self.addCleanup(unversion.finalize)
387
parent = unversion.trans_id_tree_path('parent')
388
unversion.unversion_file(parent)
389
self.assertEqual(unversion.find_conflicts(),
390
[('unversioned parent', parent_id)])
391
file_id = unversion.trans_id_tree_file_id('child-id')
392
unversion.unversion_file(file_id)
395
def test_name_invariants(self):
396
create_tree, root = self.get_transform()
398
root = create_tree.root
399
create_tree.new_file('name1', root, 'hello1', 'name1')
400
create_tree.new_file('name2', root, 'hello2', 'name2')
401
ddir = create_tree.new_directory('dying_directory', root, 'ddir')
402
create_tree.new_file('dying_file', ddir, 'goodbye1', 'dfile')
403
create_tree.new_file('moving_file', ddir, 'later1', 'mfile')
404
create_tree.new_file('moving_file2', root, 'later2', 'mfile2')
407
mangle_tree,root = self.get_transform()
408
root = mangle_tree.root
410
name1 = mangle_tree.trans_id_tree_file_id('name1')
411
name2 = mangle_tree.trans_id_tree_file_id('name2')
412
mangle_tree.adjust_path('name2', root, name1)
413
mangle_tree.adjust_path('name1', root, name2)
415
#tests for deleting parent directories
416
ddir = mangle_tree.trans_id_tree_file_id('ddir')
417
mangle_tree.delete_contents(ddir)
418
dfile = mangle_tree.trans_id_tree_file_id('dfile')
419
mangle_tree.delete_versioned(dfile)
420
mangle_tree.unversion_file(dfile)
421
mfile = mangle_tree.trans_id_tree_file_id('mfile')
422
mangle_tree.adjust_path('mfile', root, mfile)
424
#tests for adding parent directories
425
newdir = mangle_tree.new_directory('new_directory', root, 'newdir')
426
mfile2 = mangle_tree.trans_id_tree_file_id('mfile2')
427
mangle_tree.adjust_path('mfile2', newdir, mfile2)
428
mangle_tree.new_file('newfile', newdir, 'hello3', 'dfile')
429
self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
430
self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
431
self.assertEqual(mangle_tree.final_file_id(mfile2), 'mfile2')
433
self.assertEqual(file(self.wt.abspath('name1')).read(), 'hello2')
434
self.assertEqual(file(self.wt.abspath('name2')).read(), 'hello1')
435
mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
436
self.assertEqual(mangle_tree.final_parent(mfile2), newdir)
437
self.assertEqual(file(mfile2_path).read(), 'later2')
438
self.assertEqual(self.wt.id2path('mfile2'), 'new_directory/mfile2')
439
self.assertEqual(self.wt.path2id('new_directory/mfile2'), 'mfile2')
440
newfile_path = self.wt.abspath(pathjoin('new_directory','newfile'))
441
self.assertEqual(file(newfile_path).read(), 'hello3')
442
self.assertEqual(self.wt.path2id('dying_directory'), 'ddir')
443
self.assertIs(self.wt.path2id('dying_directory/dying_file'), None)
444
mfile2_path = self.wt.abspath(pathjoin('new_directory','mfile2'))
446
def test_both_rename(self):
447
create_tree,root = self.get_transform()
448
newdir = create_tree.new_directory('selftest', root, 'selftest-id')
449
create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
451
mangle_tree,root = self.get_transform()
452
selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
453
blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
454
mangle_tree.adjust_path('test', root, selftest)
455
mangle_tree.adjust_path('test_too_much', root, selftest)
456
mangle_tree.set_executability(True, blackbox)
459
def test_both_rename2(self):
460
create_tree,root = self.get_transform()
461
bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
462
tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
463
blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
464
create_tree.new_file('test_too_much.py', blackbox, 'hello1',
467
mangle_tree,root = self.get_transform()
468
bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
469
tests = mangle_tree.trans_id_tree_file_id('tests-id')
470
test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
471
mangle_tree.adjust_path('selftest', bzrlib, tests)
472
mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
473
mangle_tree.set_executability(True, test_too_much)
476
def test_both_rename3(self):
477
create_tree,root = self.get_transform()
478
tests = create_tree.new_directory('tests', root, 'tests-id')
479
create_tree.new_file('test_too_much.py', tests, 'hello1',
482
mangle_tree,root = self.get_transform()
483
tests = mangle_tree.trans_id_tree_file_id('tests-id')
484
test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
485
mangle_tree.adjust_path('selftest', root, tests)
486
mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
487
mangle_tree.set_executability(True, test_too_much)
490
def test_move_dangling_ie(self):
491
create_tree, root = self.get_transform()
493
root = create_tree.root
494
create_tree.new_file('name1', root, 'hello1', 'name1')
496
delete_contents, root = self.get_transform()
497
file = delete_contents.trans_id_tree_file_id('name1')
498
delete_contents.delete_contents(file)
499
delete_contents.apply()
500
move_id, root = self.get_transform()
501
name1 = move_id.trans_id_tree_file_id('name1')
502
newdir = move_id.new_directory('dir', root, 'newdir')
503
move_id.adjust_path('name2', newdir, name1)
506
def test_replace_dangling_ie(self):
507
create_tree, root = self.get_transform()
509
root = create_tree.root
510
create_tree.new_file('name1', root, 'hello1', 'name1')
512
delete_contents = TreeTransform(self.wt)
513
self.addCleanup(delete_contents.finalize)
514
file = delete_contents.trans_id_tree_file_id('name1')
515
delete_contents.delete_contents(file)
516
delete_contents.apply()
517
delete_contents.finalize()
518
replace = TreeTransform(self.wt)
519
self.addCleanup(replace.finalize)
520
name2 = replace.new_file('name2', root, 'hello2', 'name1')
521
conflicts = replace.find_conflicts()
522
name1 = replace.trans_id_tree_file_id('name1')
523
self.assertEqual(conflicts, [('duplicate id', name1, name2)])
524
resolve_conflicts(replace)
527
def test_symlinks(self):
528
self.requireFeature(SymlinkFeature)
529
transform,root = self.get_transform()
530
oz_id = transform.new_directory('oz', root, 'oz-id')
531
wizard = transform.new_symlink('wizard', oz_id, 'wizard-target',
533
wiz_id = transform.create_path('wizard2', oz_id)
534
transform.create_symlink('behind_curtain', wiz_id)
535
transform.version_file('wiz-id2', wiz_id)
536
transform.set_executability(True, wiz_id)
537
self.assertEqual(transform.find_conflicts(),
538
[('non-file executability', wiz_id)])
539
transform.set_executability(None, wiz_id)
541
self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
542
self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
543
self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')),
545
self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
548
def test_unable_create_symlink(self):
550
wt = self.make_branch_and_tree('.')
551
tt = TreeTransform(wt) # TreeTransform obtains write lock
553
tt.new_symlink('foo', tt.root, 'bar')
557
os_symlink = getattr(os, 'symlink', None)
560
err = self.assertRaises(errors.UnableCreateSymlink, tt_helper)
562
"Unable to create symlink 'foo' on this platform",
566
os.symlink = os_symlink
568
def get_conflicted(self):
569
create,root = self.get_transform()
570
create.new_file('dorothy', root, 'dorothy', 'dorothy-id')
571
oz = create.new_directory('oz', root, 'oz-id')
572
create.new_directory('emeraldcity', oz, 'emerald-id')
574
conflicts,root = self.get_transform()
575
# set up duplicate entry, duplicate id
576
new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
578
old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
579
oz = conflicts.trans_id_tree_file_id('oz-id')
580
# set up DeletedParent parent conflict
581
conflicts.delete_versioned(oz)
582
emerald = conflicts.trans_id_tree_file_id('emerald-id')
583
# set up MissingParent conflict
584
munchkincity = conflicts.trans_id_file_id('munchkincity-id')
585
conflicts.adjust_path('munchkincity', root, munchkincity)
586
conflicts.new_directory('auntem', munchkincity, 'auntem-id')
588
conflicts.adjust_path('emeraldcity', emerald, emerald)
589
return conflicts, emerald, oz, old_dorothy, new_dorothy
591
def test_conflict_resolution(self):
592
conflicts, emerald, oz, old_dorothy, new_dorothy =\
593
self.get_conflicted()
594
resolve_conflicts(conflicts)
595
self.assertEqual(conflicts.final_name(old_dorothy), 'dorothy.moved')
596
self.assertIs(conflicts.final_file_id(old_dorothy), None)
597
self.assertEqual(conflicts.final_name(new_dorothy), 'dorothy')
598
self.assertEqual(conflicts.final_file_id(new_dorothy), 'dorothy-id')
599
self.assertEqual(conflicts.final_parent(emerald), oz)
602
def test_cook_conflicts(self):
603
tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
604
raw_conflicts = resolve_conflicts(tt)
605
cooked_conflicts = cook_conflicts(raw_conflicts, tt)
606
duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
607
'dorothy', None, 'dorothy-id')
608
self.assertEqual(cooked_conflicts[0], duplicate)
609
duplicate_id = DuplicateID('Unversioned existing file',
610
'dorothy.moved', 'dorothy', None,
612
self.assertEqual(cooked_conflicts[1], duplicate_id)
613
missing_parent = MissingParent('Created directory', 'munchkincity',
615
deleted_parent = DeletingParent('Not deleting', 'oz', 'oz-id')
616
self.assertEqual(cooked_conflicts[2], missing_parent)
617
unversioned_parent = UnversionedParent('Versioned directory',
620
unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
622
self.assertEqual(cooked_conflicts[3], unversioned_parent)
623
parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
624
'oz/emeraldcity', 'emerald-id', 'emerald-id')
625
self.assertEqual(cooked_conflicts[4], deleted_parent)
626
self.assertEqual(cooked_conflicts[5], unversioned_parent2)
627
self.assertEqual(cooked_conflicts[6], parent_loop)
628
self.assertEqual(len(cooked_conflicts), 7)
631
def test_string_conflicts(self):
632
tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
633
raw_conflicts = resolve_conflicts(tt)
634
cooked_conflicts = cook_conflicts(raw_conflicts, tt)
636
conflicts_s = [str(c) for c in cooked_conflicts]
637
self.assertEqual(len(cooked_conflicts), len(conflicts_s))
638
self.assertEqual(conflicts_s[0], 'Conflict adding file dorothy. '
639
'Moved existing file to '
641
self.assertEqual(conflicts_s[1], 'Conflict adding id to dorothy. '
642
'Unversioned existing file '
644
self.assertEqual(conflicts_s[2], 'Conflict adding files to'
645
' munchkincity. Created directory.')
646
self.assertEqual(conflicts_s[3], 'Conflict because munchkincity is not'
647
' versioned, but has versioned'
648
' children. Versioned directory.')
649
self.assertEqualDiff(conflicts_s[4], "Conflict: can't delete oz because it"
650
" is not empty. Not deleting.")
651
self.assertEqual(conflicts_s[5], 'Conflict because oz is not'
652
' versioned, but has versioned'
653
' children. Versioned directory.')
654
self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
655
' oz/emeraldcity. Cancelled move.')
657
def prepare_wrong_parent_kind(self):
658
tt, root = self.get_transform()
659
tt.new_file('parent', root, 'contents', 'parent-id')
661
tt, root = self.get_transform()
662
parent_id = tt.trans_id_file_id('parent-id')
663
tt.new_file('child,', parent_id, 'contents2', 'file-id')
666
def test_find_conflicts_wrong_parent_kind(self):
667
tt = self.prepare_wrong_parent_kind()
670
def test_resolve_conflicts_wrong_existing_parent_kind(self):
671
tt = self.prepare_wrong_parent_kind()
672
raw_conflicts = resolve_conflicts(tt)
673
self.assertEqual(set([('non-directory parent', 'Created directory',
674
'new-3')]), raw_conflicts)
675
cooked_conflicts = cook_conflicts(raw_conflicts, tt)
676
self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
677
'parent-id')], cooked_conflicts)
679
self.assertEqual(None, self.wt.path2id('parent'))
680
self.assertEqual('parent-id', self.wt.path2id('parent.new'))
682
def test_resolve_conflicts_wrong_new_parent_kind(self):
683
tt, root = self.get_transform()
684
parent_id = tt.new_directory('parent', root, 'parent-id')
685
tt.new_file('child,', parent_id, 'contents2', 'file-id')
687
tt, root = self.get_transform()
688
parent_id = tt.trans_id_file_id('parent-id')
689
tt.delete_contents(parent_id)
690
tt.create_file('contents', parent_id)
691
raw_conflicts = resolve_conflicts(tt)
692
self.assertEqual(set([('non-directory parent', 'Created directory',
693
'new-3')]), raw_conflicts)
695
self.assertEqual(None, self.wt.path2id('parent'))
696
self.assertEqual('parent-id', self.wt.path2id('parent.new'))
698
def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
699
tt, root = self.get_transform()
700
parent_id = tt.new_directory('parent', root)
701
tt.new_file('child,', parent_id, 'contents2')
703
tt, root = self.get_transform()
704
parent_id = tt.trans_id_tree_path('parent')
705
tt.delete_contents(parent_id)
706
tt.create_file('contents', parent_id)
707
resolve_conflicts(tt)
709
self.assertIs(None, self.wt.path2id('parent'))
710
self.assertIs(None, self.wt.path2id('parent.new'))
712
def test_moving_versioned_directories(self):
713
create, root = self.get_transform()
714
kansas = create.new_directory('kansas', root, 'kansas-id')
715
create.new_directory('house', kansas, 'house-id')
716
create.new_directory('oz', root, 'oz-id')
718
cyclone, root = self.get_transform()
719
oz = cyclone.trans_id_tree_file_id('oz-id')
720
house = cyclone.trans_id_tree_file_id('house-id')
721
cyclone.adjust_path('house', oz, house)
724
def test_moving_root(self):
725
create, root = self.get_transform()
726
fun = create.new_directory('fun', root, 'fun-id')
727
create.new_directory('sun', root, 'sun-id')
728
create.new_directory('moon', root, 'moon')
730
transform, root = self.get_transform()
731
transform.adjust_root_path('oldroot', fun)
732
new_root=transform.trans_id_tree_path('')
733
transform.version_file('new-root', new_root)
736
def test_renames(self):
737
create, root = self.get_transform()
738
old = create.new_directory('old-parent', root, 'old-id')
739
intermediate = create.new_directory('intermediate', old, 'im-id')
740
myfile = create.new_file('myfile', intermediate, 'myfile-text',
743
rename, root = self.get_transform()
744
old = rename.trans_id_file_id('old-id')
745
rename.adjust_path('new', root, old)
746
myfile = rename.trans_id_file_id('myfile-id')
747
rename.set_executability(True, myfile)
750
def test_set_executability_order(self):
751
"""Ensure that executability behaves the same, no matter what order.
753
- create file and set executability simultaneously
754
- create file and set executability afterward
755
- unsetting the executability of a file whose executability has not been
756
declared should throw an exception (this may happen when a
757
merge attempts to create a file with a duplicate ID)
759
transform, root = self.get_transform()
762
self.addCleanup(wt.unlock)
763
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
765
sac = transform.new_file('set_after_creation', root,
766
'Set after creation', 'sac')
767
transform.set_executability(True, sac)
768
uws = transform.new_file('unset_without_set', root, 'Unset badly',
770
self.assertRaises(KeyError, transform.set_executability, None, uws)
772
self.assertTrue(wt.is_executable('soc'))
773
self.assertTrue(wt.is_executable('sac'))
775
def test_preserve_mode(self):
776
"""File mode is preserved when replacing content"""
777
if sys.platform == 'win32':
778
raise TestSkipped('chmod has no effect on win32')
779
transform, root = self.get_transform()
780
transform.new_file('file1', root, 'contents', 'file1-id', True)
783
self.addCleanup(self.wt.unlock)
784
self.assertTrue(self.wt.is_executable('file1-id'))
785
transform, root = self.get_transform()
786
file1_id = transform.trans_id_tree_file_id('file1-id')
787
transform.delete_contents(file1_id)
788
transform.create_file('contents2', file1_id)
790
self.assertTrue(self.wt.is_executable('file1-id'))
792
def test__set_mode_stats_correctly(self):
793
"""_set_mode stats to determine file mode."""
794
if sys.platform == 'win32':
795
raise TestSkipped('chmod has no effect on win32')
799
def instrumented_stat(path):
800
stat_paths.append(path)
801
return real_stat(path)
803
transform, root = self.get_transform()
805
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
806
file_id='bar-id-1', executable=False)
809
transform, root = self.get_transform()
810
bar1_id = transform.trans_id_tree_path('bar')
811
bar2_id = transform.trans_id_tree_path('bar2')
813
os.stat = instrumented_stat
814
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
819
bar1_abspath = self.wt.abspath('bar')
820
self.assertEqual([bar1_abspath], stat_paths)
822
def test_iter_changes(self):
823
self.wt.set_root_id('eert_toor')
824
transform, root = self.get_transform()
825
transform.new_file('old', root, 'blah', 'id-1', True)
827
transform, root = self.get_transform()
829
self.assertEqual([], list(transform.iter_changes()))
830
old = transform.trans_id_tree_file_id('id-1')
831
transform.unversion_file(old)
832
self.assertEqual([('id-1', ('old', None), False, (True, False),
833
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
834
(True, True))], list(transform.iter_changes()))
835
transform.new_directory('new', root, 'id-1')
836
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
837
('eert_toor', 'eert_toor'), ('old', 'new'),
838
('file', 'directory'),
839
(True, False))], list(transform.iter_changes()))
843
def test_iter_changes_new(self):
844
self.wt.set_root_id('eert_toor')
845
transform, root = self.get_transform()
846
transform.new_file('old', root, 'blah')
848
transform, root = self.get_transform()
850
old = transform.trans_id_tree_path('old')
851
transform.version_file('id-1', old)
852
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
853
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
854
(False, False))], list(transform.iter_changes()))
858
def test_iter_changes_modifications(self):
859
self.wt.set_root_id('eert_toor')
860
transform, root = self.get_transform()
861
transform.new_file('old', root, 'blah', 'id-1')
862
transform.new_file('new', root, 'blah')
863
transform.new_directory('subdir', root, 'subdir-id')
865
transform, root = self.get_transform()
867
old = transform.trans_id_tree_path('old')
868
subdir = transform.trans_id_tree_file_id('subdir-id')
869
new = transform.trans_id_tree_path('new')
870
self.assertEqual([], list(transform.iter_changes()))
873
transform.delete_contents(old)
874
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
875
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
876
(False, False))], list(transform.iter_changes()))
879
transform.create_file('blah', old)
880
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
881
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
882
(False, False))], list(transform.iter_changes()))
883
transform.cancel_deletion(old)
884
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
885
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
886
(False, False))], list(transform.iter_changes()))
887
transform.cancel_creation(old)
889
# move file_id to a different file
890
self.assertEqual([], list(transform.iter_changes()))
891
transform.unversion_file(old)
892
transform.version_file('id-1', new)
893
transform.adjust_path('old', root, new)
894
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
895
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
896
(False, False))], list(transform.iter_changes()))
897
transform.cancel_versioning(new)
898
transform._removed_id = set()
901
self.assertEqual([], list(transform.iter_changes()))
902
transform.set_executability(True, old)
903
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
904
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
905
(False, True))], list(transform.iter_changes()))
906
transform.set_executability(None, old)
909
self.assertEqual([], list(transform.iter_changes()))
910
transform.adjust_path('new', root, old)
911
transform._new_parent = {}
912
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
913
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
914
(False, False))], list(transform.iter_changes()))
915
transform._new_name = {}
918
self.assertEqual([], list(transform.iter_changes()))
919
transform.adjust_path('new', subdir, old)
920
transform._new_name = {}
921
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
922
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
923
('file', 'file'), (False, False))],
924
list(transform.iter_changes()))
925
transform._new_path = {}
930
def test_iter_changes_modified_bleed(self):
931
self.wt.set_root_id('eert_toor')
932
"""Modified flag should not bleed from one change to another"""
933
# unfortunately, we have no guarantee that file1 (which is modified)
934
# will be applied before file2. And if it's applied after file2, it
935
# obviously can't bleed into file2's change output. But for now, it
937
transform, root = self.get_transform()
938
transform.new_file('file1', root, 'blah', 'id-1')
939
transform.new_file('file2', root, 'blah', 'id-2')
941
transform, root = self.get_transform()
943
transform.delete_contents(transform.trans_id_file_id('id-1'))
944
transform.set_executability(True,
945
transform.trans_id_file_id('id-2'))
946
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
947
('eert_toor', 'eert_toor'), ('file1', u'file1'),
948
('file', None), (False, False)),
949
('id-2', (u'file2', u'file2'), False, (True, True),
950
('eert_toor', 'eert_toor'), ('file2', u'file2'),
951
('file', 'file'), (False, True))],
952
list(transform.iter_changes()))
956
def test_iter_changes_move_missing(self):
957
"""Test moving ids with no files around"""
958
self.wt.set_root_id('toor_eert')
959
# Need two steps because versioning a non-existant file is a conflict.
960
transform, root = self.get_transform()
961
transform.new_directory('floater', root, 'floater-id')
963
transform, root = self.get_transform()
964
transform.delete_contents(transform.trans_id_tree_path('floater'))
966
transform, root = self.get_transform()
967
floater = transform.trans_id_tree_path('floater')
969
transform.adjust_path('flitter', root, floater)
970
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
971
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
972
(None, None), (False, False))], list(transform.iter_changes()))
976
def test_iter_changes_pointless(self):
977
"""Ensure that no-ops are not treated as modifications"""
978
self.wt.set_root_id('eert_toor')
979
transform, root = self.get_transform()
980
transform.new_file('old', root, 'blah', 'id-1')
981
transform.new_directory('subdir', root, 'subdir-id')
983
transform, root = self.get_transform()
985
old = transform.trans_id_tree_path('old')
986
subdir = transform.trans_id_tree_file_id('subdir-id')
987
self.assertEqual([], list(transform.iter_changes()))
988
transform.delete_contents(subdir)
989
transform.create_directory(subdir)
990
transform.set_executability(False, old)
991
transform.unversion_file(old)
992
transform.version_file('id-1', old)
993
transform.adjust_path('old', root, old)
994
self.assertEqual([], list(transform.iter_changes()))
998
def test_rename_count(self):
999
transform, root = self.get_transform()
1000
transform.new_file('name1', root, 'contents')
1001
self.assertEqual(transform.rename_count, 0)
1003
self.assertEqual(transform.rename_count, 1)
1004
transform2, root = self.get_transform()
1005
transform2.adjust_path('name2', root,
1006
transform2.trans_id_tree_path('name1'))
1007
self.assertEqual(transform2.rename_count, 0)
1009
self.assertEqual(transform2.rename_count, 2)
1011
def test_change_parent(self):
1012
"""Ensure that after we change a parent, the results are still right.
1014
Renames and parent changes on pending transforms can happen as part
1015
of conflict resolution, and are explicitly permitted by the
1018
This test ensures they work correctly with the rename-avoidance
1021
transform, root = self.get_transform()
1022
parent1 = transform.new_directory('parent1', root)
1023
child1 = transform.new_file('child1', parent1, 'contents')
1024
parent2 = transform.new_directory('parent2', root)
1025
transform.adjust_path('child1', parent2, child1)
1027
self.failIfExists(self.wt.abspath('parent1/child1'))
1028
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1029
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1030
# no rename for child1 (counting only renames during apply)
1031
self.failUnlessEqual(2, transform.rename_count)
1033
def test_cancel_parent(self):
1034
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1036
This is like the test_change_parent, except that we cancel the parent
1037
before adjusting the path. The transform must detect that the
1038
directory is non-empty, and move children to safe locations.
1040
transform, root = self.get_transform()
1041
parent1 = transform.new_directory('parent1', root)
1042
child1 = transform.new_file('child1', parent1, 'contents')
1043
child2 = transform.new_file('child2', parent1, 'contents')
1045
transform.cancel_creation(parent1)
1047
self.fail('Failed to move child1 before deleting parent1')
1048
transform.cancel_creation(child2)
1049
transform.create_directory(parent1)
1051
transform.cancel_creation(parent1)
1052
# If the transform incorrectly believes that child2 is still in
1053
# parent1's limbo directory, it will try to rename it and fail
1054
# because was already moved by the first cancel_creation.
1056
self.fail('Transform still thinks child2 is a child of parent1')
1057
parent2 = transform.new_directory('parent2', root)
1058
transform.adjust_path('child1', parent2, child1)
1060
self.failIfExists(self.wt.abspath('parent1'))
1061
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1062
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1063
self.failUnlessEqual(2, transform.rename_count)
1065
def test_adjust_and_cancel(self):
1066
"""Make sure adjust_path keeps track of limbo children properly"""
1067
transform, root = self.get_transform()
1068
parent1 = transform.new_directory('parent1', root)
1069
child1 = transform.new_file('child1', parent1, 'contents')
1070
parent2 = transform.new_directory('parent2', root)
1071
transform.adjust_path('child1', parent2, child1)
1072
transform.cancel_creation(child1)
1074
transform.cancel_creation(parent1)
1075
# if the transform thinks child1 is still in parent1's limbo
1076
# directory, it will attempt to move it and fail.
1078
self.fail('Transform still thinks child1 is a child of parent1')
1079
transform.finalize()
1081
def test_noname_contents(self):
1082
"""TreeTransform should permit deferring naming files."""
1083
transform, root = self.get_transform()
1084
parent = transform.trans_id_file_id('parent-id')
1086
transform.create_directory(parent)
1088
self.fail("Can't handle contents with no name")
1089
transform.finalize()
1091
def test_noname_contents_nested(self):
1092
"""TreeTransform should permit deferring naming files."""
1093
transform, root = self.get_transform()
1094
parent = transform.trans_id_file_id('parent-id')
1096
transform.create_directory(parent)
1098
self.fail("Can't handle contents with no name")
1099
child = transform.new_directory('child', parent)
1100
transform.adjust_path('parent', root, parent)
1102
self.failUnlessExists(self.wt.abspath('parent/child'))
1103
self.assertEqual(1, transform.rename_count)
1105
def test_reuse_name(self):
1106
"""Avoid reusing the same limbo name for different files"""
1107
transform, root = self.get_transform()
1108
parent = transform.new_directory('parent', root)
1109
child1 = transform.new_directory('child', parent)
1111
child2 = transform.new_directory('child', parent)
1113
self.fail('Tranform tried to use the same limbo name twice')
1114
transform.adjust_path('child2', parent, child2)
1116
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1117
# child2 is put into top-level limbo because child1 has already
1118
# claimed the direct limbo path when child2 is created. There is no
1119
# advantage in renaming files once they're in top-level limbo, except
1121
self.assertEqual(2, transform.rename_count)
1123
def test_reuse_when_first_moved(self):
1124
"""Don't avoid direct paths when it is safe to use them"""
1125
transform, root = self.get_transform()
1126
parent = transform.new_directory('parent', root)
1127
child1 = transform.new_directory('child', parent)
1128
transform.adjust_path('child1', parent, child1)
1129
child2 = transform.new_directory('child', parent)
1131
# limbo/new-1 => parent
1132
self.assertEqual(1, transform.rename_count)
1134
def test_reuse_after_cancel(self):
1135
"""Don't avoid direct paths when it is safe to use them"""
1136
transform, root = self.get_transform()
1137
parent2 = transform.new_directory('parent2', root)
1138
child1 = transform.new_directory('child1', parent2)
1139
transform.cancel_creation(parent2)
1140
transform.create_directory(parent2)
1141
child2 = transform.new_directory('child1', parent2)
1142
transform.adjust_path('child2', parent2, child1)
1144
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1145
self.assertEqual(2, transform.rename_count)
1147
def test_finalize_order(self):
1148
"""Finalize must be done in child-to-parent order"""
1149
transform, root = self.get_transform()
1150
parent = transform.new_directory('parent', root)
1151
child = transform.new_directory('child', parent)
1153
transform.finalize()
1155
self.fail('Tried to remove parent before child1')
1157
def test_cancel_with_cancelled_child_should_succeed(self):
1158
transform, root = self.get_transform()
1159
parent = transform.new_directory('parent', root)
1160
child = transform.new_directory('child', parent)
1161
transform.cancel_creation(child)
1162
transform.cancel_creation(parent)
1163
transform.finalize()
1165
def test_case_insensitive_clash(self):
1166
self.requireFeature(CaseInsensitiveFilesystemFeature)
1168
wt = self.make_branch_and_tree('.')
1169
tt = TreeTransform(wt) # TreeTransform obtains write lock
1171
tt.new_file('foo', tt.root, 'bar')
1172
tt.new_file('Foo', tt.root, 'spam')
1173
# Lie to tt that we've already resolved all conflicts.
1174
tt.apply(no_conflicts=True)
1178
err = self.assertRaises(errors.FileExists, tt_helper)
1179
self.assertContainsRe(str(err),
1180
"^File exists: .+/foo")
1182
def test_two_directories_clash(self):
1184
wt = self.make_branch_and_tree('.')
1185
tt = TreeTransform(wt) # TreeTransform obtains write lock
1187
foo_1 = tt.new_directory('foo', tt.root)
1188
tt.new_directory('bar', foo_1)
1189
foo_2 = tt.new_directory('foo', tt.root)
1190
tt.new_directory('baz', foo_2)
1191
# Lie to tt that we've already resolved all conflicts.
1192
tt.apply(no_conflicts=True)
1196
err = self.assertRaises(errors.FileExists, tt_helper)
1197
self.assertContainsRe(str(err),
1198
"^File exists: .+/foo")
1200
def test_two_directories_clash_finalize(self):
1202
wt = self.make_branch_and_tree('.')
1203
tt = TreeTransform(wt) # TreeTransform obtains write lock
1205
foo_1 = tt.new_directory('foo', tt.root)
1206
tt.new_directory('bar', foo_1)
1207
foo_2 = tt.new_directory('foo', tt.root)
1208
tt.new_directory('baz', foo_2)
1209
# Lie to tt that we've already resolved all conflicts.
1210
tt.apply(no_conflicts=True)
1214
err = self.assertRaises(errors.FileExists, tt_helper)
1215
self.assertContainsRe(str(err),
1216
"^File exists: .+/foo")
1218
def test_file_to_directory(self):
1219
wt = self.make_branch_and_tree('.')
1220
self.build_tree(['foo'])
1223
tt = TreeTransform(wt)
1224
self.addCleanup(tt.finalize)
1225
foo_trans_id = tt.trans_id_tree_path("foo")
1226
tt.delete_contents(foo_trans_id)
1227
tt.create_directory(foo_trans_id)
1228
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1229
tt.create_file(["aa\n"], bar_trans_id)
1230
tt.version_file("bar-1", bar_trans_id)
1232
self.failUnlessExists("foo/bar")
1235
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1240
changes = wt.changes_from(wt.basis_tree())
1241
self.assertFalse(changes.has_changed(), changes)
1243
def test_file_to_symlink(self):
1244
self.requireFeature(SymlinkFeature)
1245
wt = self.make_branch_and_tree('.')
1246
self.build_tree(['foo'])
1249
tt = TreeTransform(wt)
1250
self.addCleanup(tt.finalize)
1251
foo_trans_id = tt.trans_id_tree_path("foo")
1252
tt.delete_contents(foo_trans_id)
1253
tt.create_symlink("bar", foo_trans_id)
1255
self.failUnlessExists("foo")
1257
self.addCleanup(wt.unlock)
1258
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1261
def test_dir_to_file(self):
1262
wt = self.make_branch_and_tree('.')
1263
self.build_tree(['foo/', 'foo/bar'])
1264
wt.add(['foo', 'foo/bar'])
1266
tt = TreeTransform(wt)
1267
self.addCleanup(tt.finalize)
1268
foo_trans_id = tt.trans_id_tree_path("foo")
1269
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1270
tt.delete_contents(foo_trans_id)
1271
tt.delete_versioned(bar_trans_id)
1272
tt.create_file(["aa\n"], foo_trans_id)
1274
self.failUnlessExists("foo")
1276
self.addCleanup(wt.unlock)
1277
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1280
def test_dir_to_hardlink(self):
1281
self.requireFeature(HardlinkFeature)
1282
wt = self.make_branch_and_tree('.')
1283
self.build_tree(['foo/', 'foo/bar'])
1284
wt.add(['foo', 'foo/bar'])
1286
tt = TreeTransform(wt)
1287
self.addCleanup(tt.finalize)
1288
foo_trans_id = tt.trans_id_tree_path("foo")
1289
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1290
tt.delete_contents(foo_trans_id)
1291
tt.delete_versioned(bar_trans_id)
1292
self.build_tree(['baz'])
1293
tt.create_hardlink("baz", foo_trans_id)
1295
self.failUnlessExists("foo")
1296
self.failUnlessExists("baz")
1298
self.addCleanup(wt.unlock)
1299
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1302
def test_no_final_path(self):
1303
transform, root = self.get_transform()
1304
trans_id = transform.trans_id_file_id('foo')
1305
transform.create_file('bar', trans_id)
1306
transform.cancel_creation(trans_id)
1310
class TransformGroup(object):
1312
def __init__(self, dirname, root_id):
1315
self.wt = BzrDir.create_standalone_workingtree(dirname)
1316
self.wt.set_root_id(root_id)
1317
self.b = self.wt.branch
1318
self.tt = TreeTransform(self.wt)
1319
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1322
def conflict_text(tree, merge):
1323
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1324
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1327
class TestTransformMerge(TestCaseInTempDir):
1329
def test_text_merge(self):
1330
root_id = generate_ids.gen_root_id()
1331
base = TransformGroup("base", root_id)
1332
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
1333
base.tt.new_file('b', base.root, 'b1', 'b')
1334
base.tt.new_file('c', base.root, 'c', 'c')
1335
base.tt.new_file('d', base.root, 'd', 'd')
1336
base.tt.new_file('e', base.root, 'e', 'e')
1337
base.tt.new_file('f', base.root, 'f', 'f')
1338
base.tt.new_directory('g', base.root, 'g')
1339
base.tt.new_directory('h', base.root, 'h')
1341
other = TransformGroup("other", root_id)
1342
other.tt.new_file('a', other.root, 'y\nb\nc\nd\be\n', 'a')
1343
other.tt.new_file('b', other.root, 'b2', 'b')
1344
other.tt.new_file('c', other.root, 'c2', 'c')
1345
other.tt.new_file('d', other.root, 'd', 'd')
1346
other.tt.new_file('e', other.root, 'e2', 'e')
1347
other.tt.new_file('f', other.root, 'f', 'f')
1348
other.tt.new_file('g', other.root, 'g', 'g')
1349
other.tt.new_file('h', other.root, 'h\ni\nj\nk\n', 'h')
1350
other.tt.new_file('i', other.root, 'h\ni\nj\nk\n', 'i')
1352
this = TransformGroup("this", root_id)
1353
this.tt.new_file('a', this.root, 'a\nb\nc\nd\bz\n', 'a')
1354
this.tt.new_file('b', this.root, 'b', 'b')
1355
this.tt.new_file('c', this.root, 'c', 'c')
1356
this.tt.new_file('d', this.root, 'd2', 'd')
1357
this.tt.new_file('e', this.root, 'e2', 'e')
1358
this.tt.new_file('f', this.root, 'f', 'f')
1359
this.tt.new_file('g', this.root, 'g', 'g')
1360
this.tt.new_file('h', this.root, '1\n2\n3\n4\n', 'h')
1361
this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1363
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1366
self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1367
# three-way text conflict
1368
self.assertEqual(this.wt.get_file('b').read(),
1369
conflict_text('b', 'b2'))
1371
self.assertEqual(this.wt.get_file('c').read(), 'c2')
1373
self.assertEqual(this.wt.get_file('d').read(), 'd2')
1374
# Ambigious clean merge
1375
self.assertEqual(this.wt.get_file('e').read(), 'e2')
1377
self.assertEqual(this.wt.get_file('f').read(), 'f')
1378
# Correct correct results when THIS == OTHER
1379
self.assertEqual(this.wt.get_file('g').read(), 'g')
1380
# Text conflict when THIS & OTHER are text and BASE is dir
1381
self.assertEqual(this.wt.get_file('h').read(),
1382
conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1383
self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1385
self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1387
self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1388
self.assertEqual(this.wt.get_file('i').read(),
1389
conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1390
self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1392
self.assertEqual(this.wt.get_file_byname('i.OTHER').read(),
1394
self.assertEqual(os.path.exists(this.wt.abspath('i.BASE')), False)
1395
modified = ['a', 'b', 'c', 'h', 'i']
1396
merge_modified = this.wt.merge_modified()
1397
self.assertSubset(merge_modified, modified)
1398
self.assertEqual(len(merge_modified), len(modified))
1399
file(this.wt.id2abspath('a'), 'wb').write('booga')
1401
merge_modified = this.wt.merge_modified()
1402
self.assertSubset(merge_modified, modified)
1403
self.assertEqual(len(merge_modified), len(modified))
1407
def test_file_merge(self):
1408
self.requireFeature(SymlinkFeature)
1409
root_id = generate_ids.gen_root_id()
1410
base = TransformGroup("BASE", root_id)
1411
this = TransformGroup("THIS", root_id)
1412
other = TransformGroup("OTHER", root_id)
1413
for tg in this, base, other:
1414
tg.tt.new_directory('a', tg.root, 'a')
1415
tg.tt.new_symlink('b', tg.root, 'b', 'b')
1416
tg.tt.new_file('c', tg.root, 'c', 'c')
1417
tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1418
targets = ((base, 'base-e', 'base-f', None, None),
1419
(this, 'other-e', 'this-f', 'other-g', 'this-h'),
1420
(other, 'other-e', None, 'other-g', 'other-h'))
1421
for tg, e_target, f_target, g_target, h_target in targets:
1422
for link, target in (('e', e_target), ('f', f_target),
1423
('g', g_target), ('h', h_target)):
1424
if target is not None:
1425
tg.tt.new_symlink(link, tg.root, target, link)
1427
for tg in this, base, other:
1429
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1430
self.assertIs(os.path.isdir(this.wt.abspath('a')), True)
1431
self.assertIs(os.path.islink(this.wt.abspath('b')), True)
1432
self.assertIs(os.path.isfile(this.wt.abspath('c')), True)
1433
for suffix in ('THIS', 'BASE', 'OTHER'):
1434
self.assertEqual(os.readlink(this.wt.abspath('d.'+suffix)), suffix)
1435
self.assertIs(os.path.lexists(this.wt.abspath('d')), False)
1436
self.assertEqual(this.wt.id2path('d'), 'd.OTHER')
1437
self.assertEqual(this.wt.id2path('f'), 'f.THIS')
1438
self.assertEqual(os.readlink(this.wt.abspath('e')), 'other-e')
1439
self.assertIs(os.path.lexists(this.wt.abspath('e.THIS')), False)
1440
self.assertIs(os.path.lexists(this.wt.abspath('e.OTHER')), False)
1441
self.assertIs(os.path.lexists(this.wt.abspath('e.BASE')), False)
1442
self.assertIs(os.path.lexists(this.wt.abspath('g')), True)
1443
self.assertIs(os.path.lexists(this.wt.abspath('g.BASE')), False)
1444
self.assertIs(os.path.lexists(this.wt.abspath('h')), False)
1445
self.assertIs(os.path.lexists(this.wt.abspath('h.BASE')), False)
1446
self.assertIs(os.path.lexists(this.wt.abspath('h.THIS')), True)
1447
self.assertIs(os.path.lexists(this.wt.abspath('h.OTHER')), True)
1449
def test_filename_merge(self):
1450
root_id = generate_ids.gen_root_id()
1451
base = TransformGroup("BASE", root_id)
1452
this = TransformGroup("THIS", root_id)
1453
other = TransformGroup("OTHER", root_id)
1454
base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1455
for t in [base, this, other]]
1456
base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1457
for t in [base, this, other]]
1458
base.tt.new_directory('c', base_a, 'c')
1459
this.tt.new_directory('c1', this_a, 'c')
1460
other.tt.new_directory('c', other_b, 'c')
1462
base.tt.new_directory('d', base_a, 'd')
1463
this.tt.new_directory('d1', this_b, 'd')
1464
other.tt.new_directory('d', other_a, 'd')
1466
base.tt.new_directory('e', base_a, 'e')
1467
this.tt.new_directory('e', this_a, 'e')
1468
other.tt.new_directory('e1', other_b, 'e')
1470
base.tt.new_directory('f', base_a, 'f')
1471
this.tt.new_directory('f1', this_b, 'f')
1472
other.tt.new_directory('f1', other_b, 'f')
1474
for tg in [this, base, other]:
1476
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1477
self.assertEqual(this.wt.id2path('c'), pathjoin('b/c1'))
1478
self.assertEqual(this.wt.id2path('d'), pathjoin('b/d1'))
1479
self.assertEqual(this.wt.id2path('e'), pathjoin('b/e1'))
1480
self.assertEqual(this.wt.id2path('f'), pathjoin('b/f1'))
1482
def test_filename_merge_conflicts(self):
1483
root_id = generate_ids.gen_root_id()
1484
base = TransformGroup("BASE", root_id)
1485
this = TransformGroup("THIS", root_id)
1486
other = TransformGroup("OTHER", root_id)
1487
base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1488
for t in [base, this, other]]
1489
base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1490
for t in [base, this, other]]
1492
base.tt.new_file('g', base_a, 'g', 'g')
1493
other.tt.new_file('g1', other_b, 'g1', 'g')
1495
base.tt.new_file('h', base_a, 'h', 'h')
1496
this.tt.new_file('h1', this_b, 'h1', 'h')
1498
base.tt.new_file('i', base.root, 'i', 'i')
1499
other.tt.new_directory('i1', this_b, 'i')
1501
for tg in [this, base, other]:
1503
Merge3Merger(this.wt, this.wt, base.wt, other.wt)
1505
self.assertEqual(this.wt.id2path('g'), pathjoin('b/g1.OTHER'))
1506
self.assertIs(os.path.lexists(this.wt.abspath('b/g1.BASE')), True)
1507
self.assertIs(os.path.lexists(this.wt.abspath('b/g1.THIS')), False)
1508
self.assertEqual(this.wt.id2path('h'), pathjoin('b/h1.THIS'))
1509
self.assertIs(os.path.lexists(this.wt.abspath('b/h1.BASE')), True)
1510
self.assertIs(os.path.lexists(this.wt.abspath('b/h1.OTHER')), False)
1511
self.assertEqual(this.wt.id2path('i'), pathjoin('b/i1.OTHER'))
1514
class TestBuildTree(tests.TestCaseWithTransport):
1516
def test_build_tree_with_symlinks(self):
1517
self.requireFeature(SymlinkFeature)
1519
a = BzrDir.create_standalone_workingtree('a')
1521
file('a/foo/bar', 'wb').write('contents')
1522
os.symlink('a/foo/bar', 'a/foo/baz')
1523
a.add(['foo', 'foo/bar', 'foo/baz'])
1524
a.commit('initial commit')
1525
b = BzrDir.create_standalone_workingtree('b')
1526
basis = a.basis_tree()
1528
self.addCleanup(basis.unlock)
1529
build_tree(basis, b)
1530
self.assertIs(os.path.isdir('b/foo'), True)
1531
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1532
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1534
def test_build_with_references(self):
1535
tree = self.make_branch_and_tree('source',
1536
format='dirstate-with-subtree')
1537
subtree = self.make_branch_and_tree('source/subtree',
1538
format='dirstate-with-subtree')
1539
tree.add_reference(subtree)
1540
tree.commit('a revision')
1541
tree.branch.create_checkout('target')
1542
self.failUnlessExists('target')
1543
self.failUnlessExists('target/subtree')
1545
def test_file_conflict_handling(self):
1546
"""Ensure that when building trees, conflict handling is done"""
1547
source = self.make_branch_and_tree('source')
1548
target = self.make_branch_and_tree('target')
1549
self.build_tree(['source/file', 'target/file'])
1550
source.add('file', 'new-file')
1551
source.commit('added file')
1552
build_tree(source.basis_tree(), target)
1553
self.assertEqual([DuplicateEntry('Moved existing file to',
1554
'file.moved', 'file', None, 'new-file')],
1556
target2 = self.make_branch_and_tree('target2')
1557
target_file = file('target2/file', 'wb')
1559
source_file = file('source/file', 'rb')
1561
target_file.write(source_file.read())
1566
build_tree(source.basis_tree(), target2)
1567
self.assertEqual([], target2.conflicts())
1569
def test_symlink_conflict_handling(self):
1570
"""Ensure that when building trees, conflict handling is done"""
1571
self.requireFeature(SymlinkFeature)
1572
source = self.make_branch_and_tree('source')
1573
os.symlink('foo', 'source/symlink')
1574
source.add('symlink', 'new-symlink')
1575
source.commit('added file')
1576
target = self.make_branch_and_tree('target')
1577
os.symlink('bar', 'target/symlink')
1578
build_tree(source.basis_tree(), target)
1579
self.assertEqual([DuplicateEntry('Moved existing file to',
1580
'symlink.moved', 'symlink', None, 'new-symlink')],
1582
target = self.make_branch_and_tree('target2')
1583
os.symlink('foo', 'target2/symlink')
1584
build_tree(source.basis_tree(), target)
1585
self.assertEqual([], target.conflicts())
1587
def test_directory_conflict_handling(self):
1588
"""Ensure that when building trees, conflict handling is done"""
1589
source = self.make_branch_and_tree('source')
1590
target = self.make_branch_and_tree('target')
1591
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1592
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1593
source.commit('added file')
1594
build_tree(source.basis_tree(), target)
1595
self.assertEqual([], target.conflicts())
1596
self.failUnlessExists('target/dir1/file')
1598
# Ensure contents are merged
1599
target = self.make_branch_and_tree('target2')
1600
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1601
build_tree(source.basis_tree(), target)
1602
self.assertEqual([], target.conflicts())
1603
self.failUnlessExists('target2/dir1/file2')
1604
self.failUnlessExists('target2/dir1/file')
1606
# Ensure new contents are suppressed for existing branches
1607
target = self.make_branch_and_tree('target3')
1608
self.make_branch('target3/dir1')
1609
self.build_tree(['target3/dir1/file2'])
1610
build_tree(source.basis_tree(), target)
1611
self.failIfExists('target3/dir1/file')
1612
self.failUnlessExists('target3/dir1/file2')
1613
self.failUnlessExists('target3/dir1.diverted/file')
1614
self.assertEqual([DuplicateEntry('Diverted to',
1615
'dir1.diverted', 'dir1', 'new-dir1', None)],
1618
target = self.make_branch_and_tree('target4')
1619
self.build_tree(['target4/dir1/'])
1620
self.make_branch('target4/dir1/file')
1621
build_tree(source.basis_tree(), target)
1622
self.failUnlessExists('target4/dir1/file')
1623
self.assertEqual('directory', file_kind('target4/dir1/file'))
1624
self.failUnlessExists('target4/dir1/file.diverted')
1625
self.assertEqual([DuplicateEntry('Diverted to',
1626
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1629
def test_mixed_conflict_handling(self):
1630
"""Ensure that when building trees, conflict handling is done"""
1631
source = self.make_branch_and_tree('source')
1632
target = self.make_branch_and_tree('target')
1633
self.build_tree(['source/name', 'target/name/'])
1634
source.add('name', 'new-name')
1635
source.commit('added file')
1636
build_tree(source.basis_tree(), target)
1637
self.assertEqual([DuplicateEntry('Moved existing file to',
1638
'name.moved', 'name', None, 'new-name')], target.conflicts())
1640
def test_raises_in_populated(self):
1641
source = self.make_branch_and_tree('source')
1642
self.build_tree(['source/name'])
1644
source.commit('added name')
1645
target = self.make_branch_and_tree('target')
1646
self.build_tree(['target/name'])
1648
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1649
build_tree, source.basis_tree(), target)
1651
def test_build_tree_rename_count(self):
1652
source = self.make_branch_and_tree('source')
1653
self.build_tree(['source/file1', 'source/dir1/'])
1654
source.add(['file1', 'dir1'])
1655
source.commit('add1')
1656
target1 = self.make_branch_and_tree('target1')
1657
transform_result = build_tree(source.basis_tree(), target1)
1658
self.assertEqual(2, transform_result.rename_count)
1660
self.build_tree(['source/dir1/file2'])
1661
source.add(['dir1/file2'])
1662
source.commit('add3')
1663
target2 = self.make_branch_and_tree('target2')
1664
transform_result = build_tree(source.basis_tree(), target2)
1665
# children of non-root directories should not be renamed
1666
self.assertEqual(2, transform_result.rename_count)
1668
def create_ab_tree(self):
1669
"""Create a committed test tree with two files"""
1670
source = self.make_branch_and_tree('source')
1671
self.build_tree_contents([('source/file1', 'A')])
1672
self.build_tree_contents([('source/file2', 'B')])
1673
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1674
source.commit('commit files')
1676
self.addCleanup(source.unlock)
1679
def test_build_tree_accelerator_tree(self):
1680
source = self.create_ab_tree()
1681
self.build_tree_contents([('source/file2', 'C')])
1683
real_source_get_file = source.get_file
1684
def get_file(file_id, path=None):
1685
calls.append(file_id)
1686
return real_source_get_file(file_id, path)
1687
source.get_file = get_file
1688
target = self.make_branch_and_tree('target')
1689
revision_tree = source.basis_tree()
1690
revision_tree.lock_read()
1691
self.addCleanup(revision_tree.unlock)
1692
build_tree(revision_tree, target, source)
1693
self.assertEqual(['file1-id'], calls)
1695
self.addCleanup(target.unlock)
1696
self.assertEqual([], list(target.iter_changes(revision_tree)))
1698
def test_build_tree_accelerator_tree_missing_file(self):
1699
source = self.create_ab_tree()
1700
os.unlink('source/file1')
1701
source.remove(['file2'])
1702
target = self.make_branch_and_tree('target')
1703
revision_tree = source.basis_tree()
1704
revision_tree.lock_read()
1705
self.addCleanup(revision_tree.unlock)
1706
build_tree(revision_tree, target, source)
1708
self.addCleanup(target.unlock)
1709
self.assertEqual([], list(target.iter_changes(revision_tree)))
1711
def test_build_tree_accelerator_wrong_kind(self):
1712
self.requireFeature(SymlinkFeature)
1713
source = self.make_branch_and_tree('source')
1714
self.build_tree_contents([('source/file1', '')])
1715
self.build_tree_contents([('source/file2', '')])
1716
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1717
source.commit('commit files')
1718
os.unlink('source/file2')
1719
self.build_tree_contents([('source/file2/', 'C')])
1720
os.unlink('source/file1')
1721
os.symlink('file2', 'source/file1')
1723
real_source_get_file = source.get_file
1724
def get_file(file_id, path=None):
1725
calls.append(file_id)
1726
return real_source_get_file(file_id, path)
1727
source.get_file = get_file
1728
target = self.make_branch_and_tree('target')
1729
revision_tree = source.basis_tree()
1730
revision_tree.lock_read()
1731
self.addCleanup(revision_tree.unlock)
1732
build_tree(revision_tree, target, source)
1733
self.assertEqual([], calls)
1735
self.addCleanup(target.unlock)
1736
self.assertEqual([], list(target.iter_changes(revision_tree)))
1738
def test_build_tree_hardlink(self):
1739
self.requireFeature(HardlinkFeature)
1740
source = self.create_ab_tree()
1741
target = self.make_branch_and_tree('target')
1742
revision_tree = source.basis_tree()
1743
revision_tree.lock_read()
1744
self.addCleanup(revision_tree.unlock)
1745
build_tree(revision_tree, target, source, hardlink=True)
1747
self.addCleanup(target.unlock)
1748
self.assertEqual([], list(target.iter_changes(revision_tree)))
1749
source_stat = os.stat('source/file1')
1750
target_stat = os.stat('target/file1')
1751
self.assertEqual(source_stat, target_stat)
1753
# Explicitly disallowing hardlinks should prevent them.
1754
target2 = self.make_branch_and_tree('target2')
1755
build_tree(revision_tree, target2, source, hardlink=False)
1757
self.addCleanup(target2.unlock)
1758
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1759
source_stat = os.stat('source/file1')
1760
target2_stat = os.stat('target2/file1')
1761
self.assertNotEqual(source_stat, target2_stat)
1763
def test_build_tree_accelerator_tree_moved(self):
1764
source = self.make_branch_and_tree('source')
1765
self.build_tree_contents([('source/file1', 'A')])
1766
source.add(['file1'], ['file1-id'])
1767
source.commit('commit files')
1768
source.rename_one('file1', 'file2')
1770
self.addCleanup(source.unlock)
1771
target = self.make_branch_and_tree('target')
1772
revision_tree = source.basis_tree()
1773
revision_tree.lock_read()
1774
self.addCleanup(revision_tree.unlock)
1775
build_tree(revision_tree, target, source)
1777
self.addCleanup(target.unlock)
1778
self.assertEqual([], list(target.iter_changes(revision_tree)))
1780
def test_build_tree_hardlinks_preserve_execute(self):
1781
self.requireFeature(HardlinkFeature)
1782
source = self.create_ab_tree()
1783
tt = TreeTransform(source)
1784
trans_id = tt.trans_id_tree_file_id('file1-id')
1785
tt.set_executability(True, trans_id)
1787
self.assertTrue(source.is_executable('file1-id'))
1788
target = self.make_branch_and_tree('target')
1789
revision_tree = source.basis_tree()
1790
revision_tree.lock_read()
1791
self.addCleanup(revision_tree.unlock)
1792
build_tree(revision_tree, target, source, hardlink=True)
1794
self.addCleanup(target.unlock)
1795
self.assertEqual([], list(target.iter_changes(revision_tree)))
1796
self.assertTrue(source.is_executable('file1-id'))
1798
def test_case_insensitive_build_tree_inventory(self):
1799
source = self.make_branch_and_tree('source')
1800
self.build_tree(['source/file', 'source/FILE'])
1801
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1802
source.commit('added files')
1803
# Don't try this at home, kids!
1804
# Force the tree to report that it is case insensitive
1805
target = self.make_branch_and_tree('target')
1806
target.case_sensitive = False
1807
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1808
self.assertEqual('file.moved', target.id2path('lower-id'))
1809
self.assertEqual('FILE', target.id2path('upper-id'))
1812
class MockTransform(object):
1814
def has_named_child(self, by_parent, parent_id, name):
1815
for child_id in by_parent[parent_id]:
1819
elif name == "name.~%s~" % child_id:
1824
class MockEntry(object):
1826
object.__init__(self)
1830
class TestGetBackupName(TestCase):
1831
def test_get_backup_name(self):
1832
tt = MockTransform()
1833
name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
1834
self.assertEqual(name, 'name.~1~')
1835
name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
1836
self.assertEqual(name, 'name.~2~')
1837
name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
1838
self.assertEqual(name, 'name.~1~')
1839
name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
1840
self.assertEqual(name, 'name.~1~')
1841
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1842
self.assertEqual(name, 'name.~4~')
1845
class TestFileMover(tests.TestCaseWithTransport):
1847
def test_file_mover(self):
1848
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1849
mover = _FileMover()
1850
mover.rename('a', 'q')
1851
self.failUnlessExists('q')
1852
self.failIfExists('a')
1853
self.failUnlessExists('q/b')
1854
self.failUnlessExists('c')
1855
self.failUnlessExists('c/d')
1857
def test_pre_delete_rollback(self):
1858
self.build_tree(['a/'])
1859
mover = _FileMover()
1860
mover.pre_delete('a', 'q')
1861
self.failUnlessExists('q')
1862
self.failIfExists('a')
1864
self.failIfExists('q')
1865
self.failUnlessExists('a')
1867
def test_apply_deletions(self):
1868
self.build_tree(['a/', 'b/'])
1869
mover = _FileMover()
1870
mover.pre_delete('a', 'q')
1871
mover.pre_delete('b', 'r')
1872
self.failUnlessExists('q')
1873
self.failUnlessExists('r')
1874
self.failIfExists('a')
1875
self.failIfExists('b')
1876
mover.apply_deletions()
1877
self.failIfExists('q')
1878
self.failIfExists('r')
1879
self.failIfExists('a')
1880
self.failIfExists('b')
1882
def test_file_mover_rollback(self):
1883
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1884
mover = _FileMover()
1885
mover.rename('c/d', 'c/f')
1886
mover.rename('c/e', 'c/d')
1888
mover.rename('a', 'c')
1889
except errors.FileExists, e:
1891
self.failUnlessExists('a')
1892
self.failUnlessExists('c/d')
1895
class Bogus(Exception):
1899
class TestTransformRollback(tests.TestCaseWithTransport):
1901
class ExceptionFileMover(_FileMover):
1903
def __init__(self, bad_source=None, bad_target=None):
1904
_FileMover.__init__(self)
1905
self.bad_source = bad_source
1906
self.bad_target = bad_target
1908
def rename(self, source, target):
1909
if (self.bad_source is not None and
1910
source.endswith(self.bad_source)):
1912
elif (self.bad_target is not None and
1913
target.endswith(self.bad_target)):
1916
_FileMover.rename(self, source, target)
1918
def test_rollback_rename(self):
1919
tree = self.make_branch_and_tree('.')
1920
self.build_tree(['a/', 'a/b'])
1921
tt = TreeTransform(tree)
1922
self.addCleanup(tt.finalize)
1923
a_id = tt.trans_id_tree_path('a')
1924
tt.adjust_path('c', tt.root, a_id)
1925
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1926
self.assertRaises(Bogus, tt.apply,
1927
_mover=self.ExceptionFileMover(bad_source='a'))
1928
self.failUnlessExists('a')
1929
self.failUnlessExists('a/b')
1931
self.failUnlessExists('c')
1932
self.failUnlessExists('c/d')
1934
def test_rollback_rename_into_place(self):
1935
tree = self.make_branch_and_tree('.')
1936
self.build_tree(['a/', 'a/b'])
1937
tt = TreeTransform(tree)
1938
self.addCleanup(tt.finalize)
1939
a_id = tt.trans_id_tree_path('a')
1940
tt.adjust_path('c', tt.root, a_id)
1941
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1942
self.assertRaises(Bogus, tt.apply,
1943
_mover=self.ExceptionFileMover(bad_target='c/d'))
1944
self.failUnlessExists('a')
1945
self.failUnlessExists('a/b')
1947
self.failUnlessExists('c')
1948
self.failUnlessExists('c/d')
1950
def test_rollback_deletion(self):
1951
tree = self.make_branch_and_tree('.')
1952
self.build_tree(['a/', 'a/b'])
1953
tt = TreeTransform(tree)
1954
self.addCleanup(tt.finalize)
1955
a_id = tt.trans_id_tree_path('a')
1956
tt.delete_contents(a_id)
1957
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1958
self.assertRaises(Bogus, tt.apply,
1959
_mover=self.ExceptionFileMover(bad_target='d'))
1960
self.failUnlessExists('a')
1961
self.failUnlessExists('a/b')
1963
def test_resolve_no_parent(self):
1964
wt = self.make_branch_and_tree('.')
1965
tt = TreeTransform(wt)
1966
self.addCleanup(tt.finalize)
1967
parent = tt.trans_id_file_id('parent-id')
1968
tt.new_file('file', parent, 'Contents')
1969
resolve_conflicts(tt)
1972
class TestTransformPreview(tests.TestCaseWithTransport):
1974
def create_tree(self):
1975
tree = self.make_branch_and_tree('.')
1976
self.build_tree_contents([('a', 'content 1')])
1977
tree.add('a', 'a-id')
1978
tree.commit('rev1', rev_id='rev1')
1979
return tree.branch.repository.revision_tree('rev1')
1981
def get_empty_preview(self):
1982
repository = self.make_repository('repo')
1983
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1984
preview = TransformPreview(tree)
1985
self.addCleanup(preview.finalize)
1988
def test_transform_preview(self):
1989
revision_tree = self.create_tree()
1990
preview = TransformPreview(revision_tree)
1991
self.addCleanup(preview.finalize)
1993
def test_transform_preview_tree(self):
1994
revision_tree = self.create_tree()
1995
preview = TransformPreview(revision_tree)
1996
self.addCleanup(preview.finalize)
1997
preview.get_preview_tree()
1999
def test_transform_new_file(self):
2000
revision_tree = self.create_tree()
2001
preview = TransformPreview(revision_tree)
2002
self.addCleanup(preview.finalize)
2003
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2004
preview_tree = preview.get_preview_tree()
2005
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2007
preview_tree.get_file('file2-id').read(), 'content B\n')
2009
def test_diff_preview_tree(self):
2010
revision_tree = self.create_tree()
2011
preview = TransformPreview(revision_tree)
2012
self.addCleanup(preview.finalize)
2013
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2014
preview_tree = preview.get_preview_tree()
2016
show_diff_trees(revision_tree, preview_tree, out)
2017
lines = out.getvalue().splitlines()
2018
self.assertEqual(lines[0], "=== added file 'file2'")
2019
# 3 lines of diff administrivia
2020
self.assertEqual(lines[4], "+content B")
2022
def test_transform_conflicts(self):
2023
revision_tree = self.create_tree()
2024
preview = TransformPreview(revision_tree)
2025
self.addCleanup(preview.finalize)
2026
preview.new_file('a', preview.root, 'content 2')
2027
resolve_conflicts(preview)
2028
trans_id = preview.trans_id_file_id('a-id')
2029
self.assertEqual('a.moved', preview.final_name(trans_id))
2031
def get_tree_and_preview_tree(self):
2032
revision_tree = self.create_tree()
2033
preview = TransformPreview(revision_tree)
2034
self.addCleanup(preview.finalize)
2035
a_trans_id = preview.trans_id_file_id('a-id')
2036
preview.delete_contents(a_trans_id)
2037
preview.create_file('b content', a_trans_id)
2038
preview_tree = preview.get_preview_tree()
2039
return revision_tree, preview_tree
2041
def test_iter_changes(self):
2042
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2043
root = revision_tree.inventory.root.file_id
2044
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2045
(root, root), ('a', 'a'), ('file', 'file'),
2047
list(preview_tree.iter_changes(revision_tree)))
2049
def test_wrong_tree_value_error(self):
2050
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2051
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2053
self.assertEqual('from_tree must be transform source tree.', str(e))
2055
def test_include_unchanged_value_error(self):
2056
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2057
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2058
revision_tree, include_unchanged=True)
2059
self.assertEqual('include_unchanged is not supported', str(e))
2061
def test_specific_files(self):
2062
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2063
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2064
revision_tree, specific_files=['pete'])
2065
self.assertEqual('specific_files is not supported', str(e))
2067
def test_want_unversioned_value_error(self):
2068
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2069
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2070
revision_tree, want_unversioned=True)
2071
self.assertEqual('want_unversioned is not supported', str(e))
2073
def test_ignore_extra_trees_no_specific_files(self):
2074
# extra_trees is harmless without specific_files, so we'll silently
2075
# accept it, even though we won't use it.
2076
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2077
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2079
def test_ignore_require_versioned_no_specific_files(self):
2080
# require_versioned is meaningless without specific_files.
2081
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2082
preview_tree.iter_changes(revision_tree, require_versioned=False)
2084
def test_ignore_pb(self):
2085
# pb could be supported, but TT.iter_changes doesn't support it.
2086
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2087
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2089
def test_kind(self):
2090
revision_tree = self.create_tree()
2091
preview = TransformPreview(revision_tree)
2092
self.addCleanup(preview.finalize)
2093
preview.new_file('file', preview.root, 'contents', 'file-id')
2094
preview.new_directory('directory', preview.root, 'dir-id')
2095
preview_tree = preview.get_preview_tree()
2096
self.assertEqual('file', preview_tree.kind('file-id'))
2097
self.assertEqual('directory', preview_tree.kind('dir-id'))
2099
def test_get_file_mtime(self):
2100
preview = self.get_empty_preview()
2101
file_trans_id = preview.new_file('file', preview.root, 'contents',
2103
limbo_path = preview._limbo_name(file_trans_id)
2104
preview_tree = preview.get_preview_tree()
2105
self.assertEqual(os.stat(limbo_path).st_mtime,
2106
preview_tree.get_file_mtime('file-id'))
2108
def test_get_file(self):
2109
preview = self.get_empty_preview()
2110
preview.new_file('file', preview.root, 'contents', 'file-id')
2111
preview_tree = preview.get_preview_tree()
2112
tree_file = preview_tree.get_file('file-id')
2114
self.assertEqual('contents', tree_file.read())
2118
def test_get_symlink_target(self):
2119
self.requireFeature(SymlinkFeature)
2120
preview = self.get_empty_preview()
2121
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2122
preview_tree = preview.get_preview_tree()
2123
self.assertEqual('target',
2124
preview_tree.get_symlink_target('symlink-id'))
2126
def test_all_file_ids(self):
2127
tree = self.make_branch_and_tree('tree')
2128
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2129
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2130
preview = TransformPreview(tree)
2131
self.addCleanup(preview.finalize)
2132
preview.unversion_file(preview.trans_id_file_id('b-id'))
2133
c_trans_id = preview.trans_id_file_id('c-id')
2134
preview.unversion_file(c_trans_id)
2135
preview.version_file('c-id', c_trans_id)
2136
preview_tree = preview.get_preview_tree()
2137
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2138
preview_tree.all_file_ids())
2140
def test_path2id_deleted_unchanged(self):
2141
tree = self.make_branch_and_tree('tree')
2142
self.build_tree(['tree/unchanged', 'tree/deleted'])
2143
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2144
preview = TransformPreview(tree)
2145
self.addCleanup(preview.finalize)
2146
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2147
preview_tree = preview.get_preview_tree()
2148
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2149
self.assertIs(None, preview_tree.path2id('deleted'))
2151
def test_path2id_created(self):
2152
tree = self.make_branch_and_tree('tree')
2153
self.build_tree(['tree/unchanged'])
2154
tree.add(['unchanged'], ['unchanged-id'])
2155
preview = TransformPreview(tree)
2156
self.addCleanup(preview.finalize)
2157
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2158
'contents', 'new-id')
2159
preview_tree = preview.get_preview_tree()
2160
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2162
def test_path2id_moved(self):
2163
tree = self.make_branch_and_tree('tree')
2164
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2165
tree.add(['old_parent', 'old_parent/child'],
2166
['old_parent-id', 'child-id'])
2167
preview = TransformPreview(tree)
2168
self.addCleanup(preview.finalize)
2169
new_parent = preview.new_directory('new_parent', preview.root,
2171
preview.adjust_path('child', new_parent,
2172
preview.trans_id_file_id('child-id'))
2173
preview_tree = preview.get_preview_tree()
2174
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2175
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2177
def test_path2id_renamed_parent(self):
2178
tree = self.make_branch_and_tree('tree')
2179
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2180
tree.add(['old_name', 'old_name/child'],
2181
['parent-id', 'child-id'])
2182
preview = TransformPreview(tree)
2183
self.addCleanup(preview.finalize)
2184
preview.adjust_path('new_name', preview.root,
2185
preview.trans_id_file_id('parent-id'))
2186
preview_tree = preview.get_preview_tree()
2187
self.assertIs(None, preview_tree.path2id('old_name/child'))
2188
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2190
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2191
preview_tree = tt.get_preview_tree()
2192
preview_result = list(preview_tree.iter_entries_by_dir(
2196
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2197
self.assertEqual(actual_result, preview_result)
2199
def test_iter_entries_by_dir_new(self):
2200
tree = self.make_branch_and_tree('tree')
2201
tt = TreeTransform(tree)
2202
tt.new_file('new', tt.root, 'contents', 'new-id')
2203
self.assertMatchingIterEntries(tt)
2205
def test_iter_entries_by_dir_deleted(self):
2206
tree = self.make_branch_and_tree('tree')
2207
self.build_tree(['tree/deleted'])
2208
tree.add('deleted', 'deleted-id')
2209
tt = TreeTransform(tree)
2210
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2211
self.assertMatchingIterEntries(tt)
2213
def test_iter_entries_by_dir_unversioned(self):
2214
tree = self.make_branch_and_tree('tree')
2215
self.build_tree(['tree/removed'])
2216
tree.add('removed', 'removed-id')
2217
tt = TreeTransform(tree)
2218
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2219
self.assertMatchingIterEntries(tt)
2221
def test_iter_entries_by_dir_moved(self):
2222
tree = self.make_branch_and_tree('tree')
2223
self.build_tree(['tree/moved', 'tree/new_parent/'])
2224
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2225
tt = TreeTransform(tree)
2226
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2227
tt.trans_id_file_id('moved-id'))
2228
self.assertMatchingIterEntries(tt)
2230
def test_iter_entries_by_dir_specific_file_ids(self):
2231
tree = self.make_branch_and_tree('tree')
2232
tree.set_root_id('tree-root-id')
2233
self.build_tree(['tree/parent/', 'tree/parent/child'])
2234
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2235
tt = TreeTransform(tree)
2236
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2238
def test_symlink_content_summary(self):
2239
self.requireFeature(SymlinkFeature)
2240
preview = self.get_empty_preview()
2241
preview.new_symlink('path', preview.root, 'target', 'path-id')
2242
summary = preview.get_preview_tree().path_content_summary('path')
2243
self.assertEqual(('symlink', None, None, 'target'), summary)
2245
def test_missing_content_summary(self):
2246
preview = self.get_empty_preview()
2247
summary = preview.get_preview_tree().path_content_summary('path')
2248
self.assertEqual(('missing', None, None, None), summary)
2250
def test_deleted_content_summary(self):
2251
tree = self.make_branch_and_tree('tree')
2252
self.build_tree(['tree/path/'])
2254
preview = TransformPreview(tree)
2255
self.addCleanup(preview.finalize)
2256
preview.delete_contents(preview.trans_id_tree_path('path'))
2257
summary = preview.get_preview_tree().path_content_summary('path')
2258
self.assertEqual(('missing', None, None, None), summary)
2260
def test_file_content_summary_executable(self):
2261
if not osutils.supports_executable():
2262
raise TestNotApplicable()
2263
preview = self.get_empty_preview()
2264
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2265
preview.set_executability(True, path_id)
2266
summary = preview.get_preview_tree().path_content_summary('path')
2267
self.assertEqual(4, len(summary))
2268
self.assertEqual('file', summary[0])
2269
# size must be known
2270
self.assertEqual(len('contents'), summary[1])
2272
self.assertEqual(True, summary[2])
2273
# will not have hash (not cheap to determine)
2274
self.assertIs(None, summary[3])
2276
def test_change_executability(self):
2277
if not osutils.supports_executable():
2278
raise TestNotApplicable()
2279
tree = self.make_branch_and_tree('tree')
2280
self.build_tree(['tree/path'])
2282
preview = TransformPreview(tree)
2283
self.addCleanup(preview.finalize)
2284
path_id = preview.trans_id_tree_path('path')
2285
preview.set_executability(True, path_id)
2286
summary = preview.get_preview_tree().path_content_summary('path')
2287
self.assertEqual(True, summary[2])
2289
def test_file_content_summary_non_exec(self):
2290
preview = self.get_empty_preview()
2291
preview.new_file('path', preview.root, 'contents', 'path-id')
2292
summary = preview.get_preview_tree().path_content_summary('path')
2293
self.assertEqual(4, len(summary))
2294
self.assertEqual('file', summary[0])
2295
# size must be known
2296
self.assertEqual(len('contents'), summary[1])
2298
if osutils.supports_executable():
2299
self.assertEqual(False, summary[2])
2301
self.assertEqual(None, summary[2])
2302
# will not have hash (not cheap to determine)
2303
self.assertIs(None, summary[3])
2305
def test_dir_content_summary(self):
2306
preview = self.get_empty_preview()
2307
preview.new_directory('path', preview.root, 'path-id')
2308
summary = preview.get_preview_tree().path_content_summary('path')
2309
self.assertEqual(('directory', None, None, None), summary)
2311
def test_tree_content_summary(self):
2312
preview = self.get_empty_preview()
2313
path = preview.new_directory('path', preview.root, 'path-id')
2314
preview.set_tree_reference('rev-1', path)
2315
summary = preview.get_preview_tree().path_content_summary('path')
2316
self.assertEqual(4, len(summary))
2317
self.assertEqual('tree-reference', summary[0])
2319
def test_annotate(self):
2320
tree = self.make_branch_and_tree('tree')
2321
self.build_tree_contents([('tree/file', 'a\n')])
2322
tree.add('file', 'file-id')
2323
tree.commit('a', rev_id='one')
2324
self.build_tree_contents([('tree/file', 'a\nb\n')])
2325
preview = TransformPreview(tree)
2326
self.addCleanup(preview.finalize)
2327
file_trans_id = preview.trans_id_file_id('file-id')
2328
preview.delete_contents(file_trans_id)
2329
preview.create_file('a\nb\nc\n', file_trans_id)
2330
preview_tree = preview.get_preview_tree()
2336
annotation = preview_tree.annotate_iter('file-id', 'me:')
2337
self.assertEqual(expected, annotation)
2339
def test_annotate_missing(self):
2340
preview = self.get_empty_preview()
2341
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2342
preview_tree = preview.get_preview_tree()
2348
annotation = preview_tree.annotate_iter('file-id', 'me:')
2349
self.assertEqual(expected, annotation)
2351
def test_annotate_rename(self):
2352
tree = self.make_branch_and_tree('tree')
2353
self.build_tree_contents([('tree/file', 'a\n')])
2354
tree.add('file', 'file-id')
2355
tree.commit('a', rev_id='one')
2356
preview = TransformPreview(tree)
2357
self.addCleanup(preview.finalize)
2358
file_trans_id = preview.trans_id_file_id('file-id')
2359
preview.adjust_path('newname', preview.root, file_trans_id)
2360
preview_tree = preview.get_preview_tree()
2364
annotation = preview_tree.annotate_iter('file-id', 'me:')
2365
self.assertEqual(expected, annotation)
2367
def test_annotate_deleted(self):
2368
tree = self.make_branch_and_tree('tree')
2369
self.build_tree_contents([('tree/file', 'a\n')])
2370
tree.add('file', 'file-id')
2371
tree.commit('a', rev_id='one')
2372
self.build_tree_contents([('tree/file', 'a\nb\n')])
2373
preview = TransformPreview(tree)
2374
self.addCleanup(preview.finalize)
2375
file_trans_id = preview.trans_id_file_id('file-id')
2376
preview.delete_contents(file_trans_id)
2377
preview_tree = preview.get_preview_tree()
2378
annotation = preview_tree.annotate_iter('file-id', 'me:')
2379
self.assertIs(None, annotation)
2381
def test_stored_kind(self):
2382
preview = self.get_empty_preview()
2383
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2384
preview_tree = preview.get_preview_tree()
2385
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2387
def test_is_executable(self):
2388
preview = self.get_empty_preview()
2389
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2390
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2391
preview_tree = preview.get_preview_tree()
2392
self.assertEqual(True, preview_tree.is_executable('file-id'))
2394
def test_get_set_parent_ids(self):
2395
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2396
self.assertEqual([], preview_tree.get_parent_ids())
2397
preview_tree.set_parent_ids(['rev-1'])
2398
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2400
def test_plan_file_merge(self):
2401
work_a = self.make_branch_and_tree('wta')
2402
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2403
work_a.add('file', 'file-id')
2404
base_id = work_a.commit('base version')
2405
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2406
preview = TransformPreview(work_a)
2407
self.addCleanup(preview.finalize)
2408
trans_id = preview.trans_id_file_id('file-id')
2409
preview.delete_contents(trans_id)
2410
preview.create_file('b\nc\nd\ne\n', trans_id)
2411
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2412
tree_a = preview.get_preview_tree()
2413
tree_a.set_parent_ids([base_id])
2415
('killed-a', 'a\n'),
2416
('killed-b', 'b\n'),
2417
('unchanged', 'c\n'),
2418
('unchanged', 'd\n'),
2421
], list(tree_a.plan_file_merge('file-id', tree_b)))
2423
def test_plan_file_merge_revision_tree(self):
2424
work_a = self.make_branch_and_tree('wta')
2425
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2426
work_a.add('file', 'file-id')
2427
base_id = work_a.commit('base version')
2428
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2429
preview = TransformPreview(work_a.basis_tree())
2430
self.addCleanup(preview.finalize)
2431
trans_id = preview.trans_id_file_id('file-id')
2432
preview.delete_contents(trans_id)
2433
preview.create_file('b\nc\nd\ne\n', trans_id)
2434
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2435
tree_a = preview.get_preview_tree()
2436
tree_a.set_parent_ids([base_id])
2438
('killed-a', 'a\n'),
2439
('killed-b', 'b\n'),
2440
('unchanged', 'c\n'),
2441
('unchanged', 'd\n'),
2444
], list(tree_a.plan_file_merge('file-id', tree_b)))
2446
def test_walkdirs(self):
2447
preview = self.get_empty_preview()
2448
preview.version_file('tree-root', preview.root)
2449
preview_tree = preview.get_preview_tree()
2450
file_trans_id = preview.new_file('a', preview.root, 'contents',
2452
expected = [(('', 'tree-root'),
2453
[('a', 'a', 'file', None, 'a-id', 'file')])]
2454
self.assertEqual(expected, list(preview_tree.walkdirs()))