~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
import stat
 
18
from StringIO import StringIO
19
19
import sys
 
20
import time
20
21
 
21
22
from bzrlib import (
 
23
    bencode,
22
24
    errors,
 
25
    filters,
23
26
    generate_ids,
24
 
    symbol_versioning,
 
27
    osutils,
 
28
    progress,
 
29
    revision as _mod_revision,
 
30
    rules,
25
31
    tests,
26
32
    urlutils,
27
33
    )
28
34
from bzrlib.bzrdir import BzrDir
29
35
from bzrlib.conflicts import (DuplicateEntry, DuplicateID, MissingParent,
30
 
                              UnversionedParent, ParentLoop, DeletingParent,)
 
36
                              UnversionedParent, ParentLoop, DeletingParent,
 
37
                              NonDirectoryParent)
 
38
from bzrlib.diff import show_diff_trees
31
39
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
32
 
                           ReusingTransform, CantMoveRoot, 
 
40
                           ReusingTransform, CantMoveRoot,
33
41
                           PathsNotVersionedError, ExistingLimbo,
34
42
                           ExistingPendingDeletion, ImmortalLimbo,
35
43
                           ImmortalPendingDeletion, LockError)
36
44
from bzrlib.osutils import file_kind, pathjoin
37
 
from bzrlib.merge import Merge3Merger
 
45
from bzrlib.merge import Merge3Merger, Merger
38
46
from bzrlib.tests import (
39
 
    CaseInsensitiveFilesystemFeature,
 
47
    HardlinkFeature,
40
48
    SymlinkFeature,
41
49
    TestCase,
42
50
    TestCaseInTempDir,
43
51
    TestSkipped,
44
52
    )
45
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
46
 
                              resolve_conflicts, cook_conflicts, 
47
 
                              find_interesting, build_tree, get_backup_name,
48
 
                              change_entry, _FileMover, resolve_checkout)
 
53
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
54
                              resolve_conflicts, cook_conflicts,
 
55
                              build_tree, get_backup_name,
 
56
                              _FileMover, resolve_checkout,
 
57
                              TransformPreview, create_from_tree)
49
58
 
50
59
 
51
60
class TestTreeTransform(tests.TestCaseWithTransport):
57
66
 
58
67
    def get_transform(self):
59
68
        transform = TreeTransform(self.wt)
60
 
        #self.addCleanup(transform.finalize)
 
69
        self.addCleanup(transform.finalize)
61
70
        return transform, transform.root
62
71
 
63
72
    def test_existing_limbo(self):
78
87
    def test_existing_pending_deletion(self):
79
88
        transform, root = self.get_transform()
80
89
        deletion_path = self._limbodir = urlutils.local_path_from_url(
81
 
            transform._tree._control_files.controlfilename('pending-deletion'))
 
90
            transform._tree._transport.abspath('pending-deletion'))
82
91
        os.mkdir(pathjoin(deletion_path, 'blocking-directory'))
83
92
        self.assertRaises(ImmortalPendingDeletion, transform.apply)
84
93
        self.assertRaises(LockError, self.wt.unlock)
121
130
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
122
131
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
123
132
        self.assertEqual(len(modified_paths), 3)
124
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
133
        tree_mod_paths = [self.wt.id2abspath(f) for f in
125
134
                          ('ozzie', 'my_pretties', 'my_pretties2')]
126
135
        self.assertSubset(tree_mod_paths, modified_paths)
127
136
        # is it safe to finalize repeatedly?
128
137
        transform.finalize()
129
138
        transform.finalize()
130
139
 
 
140
    def test_create_files_same_timestamp(self):
 
141
        transform, root = self.get_transform()
 
142
        self.wt.lock_tree_write()
 
143
        self.addCleanup(self.wt.unlock)
 
144
        # Roll back the clock, so that we know everything is being set to the
 
145
        # exact time
 
146
        transform._creation_mtime = creation_mtime = time.time() - 20.0
 
147
        transform.create_file('content-one',
 
148
                              transform.create_path('one', root))
 
149
        time.sleep(1) # *ugly*
 
150
        transform.create_file('content-two',
 
151
                              transform.create_path('two', root))
 
152
        transform.apply()
 
153
        fo, st1 = self.wt.get_file_with_stat(None, path='one', filtered=False)
 
154
        fo.close()
 
155
        fo, st2 = self.wt.get_file_with_stat(None, path='two', filtered=False)
 
156
        fo.close()
 
157
        # We only guarantee 2s resolution
 
158
        self.assertTrue(abs(creation_mtime - st1.st_mtime) < 2.0,
 
159
            "%s != %s within 2 seconds" % (creation_mtime, st1.st_mtime))
 
160
        # But if we have more than that, all files should get the same result
 
161
        self.assertEqual(st1.st_mtime, st2.st_mtime)
 
162
 
 
163
    def test_change_root_id(self):
 
164
        transform, root = self.get_transform()
 
165
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
166
        transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
167
        transform.delete_contents(root)
 
168
        transform.unversion_file(root)
 
169
        transform.fixup_new_roots()
 
170
        transform.apply()
 
171
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
172
 
 
173
    def test_change_root_id_add_files(self):
 
174
        transform, root = self.get_transform()
 
175
        self.assertNotEqual('new-root-id', self.wt.get_root_id())
 
176
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
177
        transform.new_file('file', new_trans_id, ['new-contents\n'],
 
178
                           'new-file-id')
 
179
        transform.delete_contents(root)
 
180
        transform.unversion_file(root)
 
181
        transform.fixup_new_roots()
 
182
        transform.apply()
 
183
        self.assertEqual('new-root-id', self.wt.get_root_id())
 
184
        self.assertEqual('new-file-id', self.wt.path2id('file'))
 
185
        self.assertFileEqual('new-contents\n', self.wt.abspath('file'))
 
186
 
 
187
    def test_add_two_roots(self):
 
188
        transform, root = self.get_transform()
 
189
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'new-root-id')
 
190
        new_trans_id = transform.new_directory('', ROOT_PARENT, 'alt-root-id')
 
191
        self.assertRaises(ValueError, transform.fixup_new_roots)
 
192
 
 
193
    def test_hardlink(self):
 
194
        self.requireFeature(HardlinkFeature)
 
195
        transform, root = self.get_transform()
 
196
        transform.new_file('file1', root, 'contents')
 
197
        transform.apply()
 
198
        target = self.make_branch_and_tree('target')
 
199
        target_transform = TreeTransform(target)
 
200
        trans_id = target_transform.create_path('file1', target_transform.root)
 
201
        target_transform.create_hardlink(self.wt.abspath('file1'), trans_id)
 
202
        target_transform.apply()
 
203
        self.failUnlessExists('target/file1')
 
204
        source_stat = os.stat(self.wt.abspath('file1'))
 
205
        target_stat = os.stat('target/file1')
 
206
        self.assertEqual(source_stat, target_stat)
 
207
 
131
208
    def test_convenience(self):
132
209
        transform, root = self.get_transform()
133
210
        self.wt.lock_tree_write()
134
211
        self.addCleanup(self.wt.unlock)
135
 
        trans_id = transform.new_file('name', root, 'contents', 
 
212
        trans_id = transform.new_file('name', root, 'contents',
136
213
                                      'my_pretties', True)
137
214
        oz = transform.new_directory('oz', root, 'oz-id')
138
215
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
139
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
216
        toto = transform.new_file('toto', dorothy, 'toto-contents',
140
217
                                  'toto-id', False)
141
218
 
142
219
        self.assertEqual(len(transform.find_conflicts()), 0)
166
243
 
167
244
    def test_conflicts(self):
168
245
        transform, root = self.get_transform()
169
 
        trans_id = transform.new_file('name', root, 'contents', 
 
246
        trans_id = transform.new_file('name', root, 'contents',
170
247
                                      'my_pretties')
171
248
        self.assertEqual(len(transform.find_conflicts()), 0)
172
249
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
173
 
        self.assertEqual(transform.find_conflicts(), 
 
250
        self.assertEqual(transform.find_conflicts(),
174
251
                         [('duplicate', trans_id, trans_id2, 'name')])
175
252
        self.assertRaises(MalformedTransform, transform.apply)
176
253
        transform.adjust_path('name', trans_id, trans_id2)
177
 
        self.assertEqual(transform.find_conflicts(), 
 
254
        self.assertEqual(transform.find_conflicts(),
178
255
                         [('non-directory parent', trans_id)])
179
256
        tinman_id = transform.trans_id_tree_path('tinman')
180
257
        transform.adjust_path('name', tinman_id, trans_id2)
181
 
        self.assertEqual(transform.find_conflicts(), 
182
 
                         [('unversioned parent', tinman_id), 
 
258
        self.assertEqual(transform.find_conflicts(),
 
259
                         [('unversioned parent', tinman_id),
183
260
                          ('missing parent', tinman_id)])
184
261
        lion_id = transform.create_path('lion', root)
185
 
        self.assertEqual(transform.find_conflicts(), 
186
 
                         [('unversioned parent', tinman_id), 
 
262
        self.assertEqual(transform.find_conflicts(),
 
263
                         [('unversioned parent', tinman_id),
187
264
                          ('missing parent', tinman_id)])
188
265
        transform.adjust_path('name', lion_id, trans_id2)
189
 
        self.assertEqual(transform.find_conflicts(), 
 
266
        self.assertEqual(transform.find_conflicts(),
190
267
                         [('unversioned parent', lion_id),
191
268
                          ('missing parent', lion_id)])
192
269
        transform.version_file("Courage", lion_id)
193
 
        self.assertEqual(transform.find_conflicts(), 
194
 
                         [('missing parent', lion_id), 
 
270
        self.assertEqual(transform.find_conflicts(),
 
271
                         [('missing parent', lion_id),
195
272
                          ('versioning no contents', lion_id)])
196
273
        transform.adjust_path('name2', root, trans_id2)
197
 
        self.assertEqual(transform.find_conflicts(), 
 
274
        self.assertEqual(transform.find_conflicts(),
198
275
                         [('versioning no contents', lion_id)])
199
276
        transform.create_file('Contents, okay?', lion_id)
200
277
        transform.adjust_path('name2', trans_id2, trans_id2)
201
 
        self.assertEqual(transform.find_conflicts(), 
202
 
                         [('parent loop', trans_id2), 
 
278
        self.assertEqual(transform.find_conflicts(),
 
279
                         [('parent loop', trans_id2),
203
280
                          ('non-directory parent', trans_id2)])
204
281
        transform.adjust_path('name2', root, trans_id2)
205
282
        oz_id = transform.new_directory('oz', root)
206
283
        transform.set_executability(True, oz_id)
207
 
        self.assertEqual(transform.find_conflicts(), 
 
284
        self.assertEqual(transform.find_conflicts(),
208
285
                         [('unversioned executability', oz_id)])
209
286
        transform.version_file('oz-id', oz_id)
210
 
        self.assertEqual(transform.find_conflicts(), 
 
287
        self.assertEqual(transform.find_conflicts(),
211
288
                         [('non-file executability', oz_id)])
212
289
        transform.set_executability(None, oz_id)
213
290
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
222
299
        self.assert_('oz/tip' in transform2._tree_path_ids)
223
300
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
224
301
        self.assertEqual(len(result), 2)
225
 
        self.assertEqual((result[0][0], result[0][1]), 
 
302
        self.assertEqual((result[0][0], result[0][1]),
226
303
                         ('duplicate', newtip))
227
 
        self.assertEqual((result[1][0], result[1][2]), 
 
304
        self.assertEqual((result[1][0], result[1][2]),
228
305
                         ('duplicate id', newtip))
229
306
        transform2.finalize()
230
307
        transform3 = TreeTransform(self.wt)
231
308
        self.addCleanup(transform3.finalize)
232
309
        oz_id = transform3.trans_id_tree_file_id('oz-id')
233
310
        transform3.delete_contents(oz_id)
234
 
        self.assertEqual(transform3.find_conflicts(), 
 
311
        self.assertEqual(transform3.find_conflicts(),
235
312
                         [('missing parent', oz_id)])
236
313
        root_id = transform3.root
237
314
        tip_id = transform3.trans_id_tree_file_id('tip-id')
250
327
        transform.new_file('FiLe', transform.root, 'content')
251
328
        result = transform.find_conflicts()
252
329
        self.assertEqual([], result)
 
330
        transform.finalize()
253
331
        # Force the tree to report that it is case insensitive, for conflict
254
332
        # generation tests
255
333
        tree.case_sensitive = False
 
334
        transform = TreeTransform(tree)
 
335
        self.addCleanup(transform.finalize)
 
336
        transform.new_file('file', transform.root, 'content')
 
337
        transform.new_file('FiLe', transform.root, 'content')
256
338
        result = transform.find_conflicts()
257
339
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
258
340
 
268
350
        transform.new_file('file', transform.root, 'content')
269
351
        result = transform.find_conflicts()
270
352
        self.assertEqual([], result)
 
353
        transform.finalize()
271
354
        # Force the tree to report that it is case insensitive, for conflict
272
355
        # generation tests
273
356
        tree.case_sensitive = False
 
357
        transform = TreeTransform(tree)
 
358
        self.addCleanup(transform.finalize)
 
359
        transform.new_file('file', transform.root, 'content')
274
360
        result = transform.find_conflicts()
275
361
        self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
276
362
 
337
423
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
338
424
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
339
425
 
 
426
    def test_adjust_path_updates_child_limbo_names(self):
 
427
        tree = self.make_branch_and_tree('tree')
 
428
        transform = TreeTransform(tree)
 
429
        self.addCleanup(transform.finalize)
 
430
        foo_id = transform.new_directory('foo', transform.root)
 
431
        bar_id = transform.new_directory('bar', foo_id)
 
432
        baz_id = transform.new_directory('baz', bar_id)
 
433
        qux_id = transform.new_directory('qux', baz_id)
 
434
        transform.adjust_path('quxx', foo_id, bar_id)
 
435
        self.assertStartsWith(transform._limbo_name(qux_id),
 
436
                              transform._limbo_name(bar_id))
 
437
 
340
438
    def test_add_del(self):
341
439
        start, root = self.get_transform()
342
440
        start.new_directory('a', root, 'a')
355
453
        self.addCleanup(unversion.finalize)
356
454
        parent = unversion.trans_id_tree_path('parent')
357
455
        unversion.unversion_file(parent)
358
 
        self.assertEqual(unversion.find_conflicts(), 
 
456
        self.assertEqual(unversion.find_conflicts(),
359
457
                         [('unversioned parent', parent_id)])
360
458
        file_id = unversion.trans_id_tree_file_id('child-id')
361
459
        unversion.unversion_file(file_id)
381
479
        mangle_tree.adjust_path('name2', root, name1)
382
480
        mangle_tree.adjust_path('name1', root, name2)
383
481
 
384
 
        #tests for deleting parent directories 
 
482
        #tests for deleting parent directories
385
483
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
386
484
        mangle_tree.delete_contents(ddir)
387
485
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
416
514
        create_tree,root = self.get_transform()
417
515
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
418
516
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
419
 
        create_tree.apply()        
 
517
        create_tree.apply()
420
518
        mangle_tree,root = self.get_transform()
421
519
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
422
520
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
430
528
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
431
529
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
432
530
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
433
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
531
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
434
532
                             'test_too_much-id')
435
 
        create_tree.apply()        
 
533
        create_tree.apply()
436
534
        mangle_tree,root = self.get_transform()
437
535
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
438
536
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
439
537
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
440
538
        mangle_tree.adjust_path('selftest', bzrlib, tests)
441
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
539
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
442
540
        mangle_tree.set_executability(True, test_too_much)
443
541
        mangle_tree.apply()
444
542
 
445
543
    def test_both_rename3(self):
446
544
        create_tree,root = self.get_transform()
447
545
        tests = create_tree.new_directory('tests', root, 'tests-id')
448
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
546
        create_tree.new_file('test_too_much.py', tests, 'hello1',
449
547
                             'test_too_much-id')
450
 
        create_tree.apply()        
 
548
        create_tree.apply()
451
549
        mangle_tree,root = self.get_transform()
452
550
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
453
551
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
454
552
        mangle_tree.adjust_path('selftest', root, tests)
455
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
553
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
456
554
        mangle_tree.set_executability(True, test_too_much)
457
555
        mangle_tree.apply()
458
556
 
471
569
        newdir = move_id.new_directory('dir', root, 'newdir')
472
570
        move_id.adjust_path('name2', newdir, name1)
473
571
        move_id.apply()
474
 
        
 
572
 
475
573
    def test_replace_dangling_ie(self):
476
574
        create_tree, root = self.get_transform()
477
575
        # prepare tree
493
591
        resolve_conflicts(replace)
494
592
        replace.apply()
495
593
 
496
 
    def test_symlinks(self):
 
594
    def _test_symlinks(self, link_name1,link_target1,
 
595
                       link_name2, link_target2):
 
596
 
 
597
        def ozpath(p): return 'oz/' + p
 
598
 
497
599
        self.requireFeature(SymlinkFeature)
498
 
        transform,root = self.get_transform()
 
600
        transform, root = self.get_transform()
499
601
        oz_id = transform.new_directory('oz', root, 'oz-id')
500
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
602
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
501
603
                                       'wizard-id')
502
 
        wiz_id = transform.create_path('wizard2', oz_id)
503
 
        transform.create_symlink('behind_curtain', wiz_id)
504
 
        transform.version_file('wiz-id2', wiz_id)            
 
604
        wiz_id = transform.create_path(link_name2, oz_id)
 
605
        transform.create_symlink(link_target2, wiz_id)
 
606
        transform.version_file('wiz-id2', wiz_id)
505
607
        transform.set_executability(True, wiz_id)
506
 
        self.assertEqual(transform.find_conflicts(), 
 
608
        self.assertEqual(transform.find_conflicts(),
507
609
                         [('non-file executability', wiz_id)])
508
610
        transform.set_executability(None, wiz_id)
509
611
        transform.apply()
510
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
511
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
512
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
513
 
                         'behind_curtain')
514
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
515
 
                         'wizard-target')
 
612
        self.assertEqual(self.wt.path2id(ozpath(link_name1)), 'wizard-id')
 
613
        self.assertEqual('symlink',
 
614
                         file_kind(self.wt.abspath(ozpath(link_name1))))
 
615
        self.assertEqual(link_target2,
 
616
                         osutils.readlink(self.wt.abspath(ozpath(link_name2))))
 
617
        self.assertEqual(link_target1,
 
618
                         osutils.readlink(self.wt.abspath(ozpath(link_name1))))
 
619
 
 
620
    def test_symlinks(self):
 
621
        self._test_symlinks('wizard', 'wizard-target',
 
622
                            'wizard2', 'behind_curtain')
 
623
 
 
624
    def test_symlinks_unicode(self):
 
625
        self.requireFeature(tests.UnicodeFilenameFeature)
 
626
        self._test_symlinks(u'\N{Euro Sign}wizard',
 
627
                            u'wizard-targ\N{Euro Sign}t',
 
628
                            u'\N{Euro Sign}wizard2',
 
629
                            u'b\N{Euro Sign}hind_curtain')
516
630
 
517
631
    def test_unable_create_symlink(self):
518
632
        def tt_helper():
542
656
        create.apply()
543
657
        conflicts,root = self.get_transform()
544
658
        # set up duplicate entry, duplicate id
545
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
659
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
546
660
                                         'dorothy-id')
547
661
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
548
662
        oz = conflicts.trans_id_tree_file_id('oz-id')
572
686
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
573
687
        raw_conflicts = resolve_conflicts(tt)
574
688
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
575
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
689
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
576
690
                                   'dorothy', None, 'dorothy-id')
577
691
        self.assertEqual(cooked_conflicts[0], duplicate)
578
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
692
        duplicate_id = DuplicateID('Unversioned existing file',
579
693
                                   'dorothy.moved', 'dorothy', None,
580
694
                                   'dorothy-id')
581
695
        self.assertEqual(cooked_conflicts[1], duplicate_id)
589
703
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
590
704
                                               'oz-id')
591
705
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
592
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
706
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
593
707
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
594
708
        self.assertEqual(cooked_conflicts[4], deleted_parent)
595
709
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
621
735
                                         ' versioned, but has versioned'
622
736
                                         ' children.  Versioned directory.')
623
737
        self.assertEqual(conflicts_s[6], 'Conflict moving oz/emeraldcity into'
624
 
                                         ' oz/emeraldcity.  Cancelled move.')
 
738
                                         ' oz/emeraldcity. Cancelled move.')
 
739
 
 
740
    def prepare_wrong_parent_kind(self):
 
741
        tt, root = self.get_transform()
 
742
        tt.new_file('parent', root, 'contents', 'parent-id')
 
743
        tt.apply()
 
744
        tt, root = self.get_transform()
 
745
        parent_id = tt.trans_id_file_id('parent-id')
 
746
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
747
        return tt
 
748
 
 
749
    def test_find_conflicts_wrong_parent_kind(self):
 
750
        tt = self.prepare_wrong_parent_kind()
 
751
        tt.find_conflicts()
 
752
 
 
753
    def test_resolve_conflicts_wrong_existing_parent_kind(self):
 
754
        tt = self.prepare_wrong_parent_kind()
 
755
        raw_conflicts = resolve_conflicts(tt)
 
756
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
757
                         'new-3')]), raw_conflicts)
 
758
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
 
759
        self.assertEqual([NonDirectoryParent('Created directory', 'parent.new',
 
760
        'parent-id')], cooked_conflicts)
 
761
        tt.apply()
 
762
        self.assertEqual(None, self.wt.path2id('parent'))
 
763
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
764
 
 
765
    def test_resolve_conflicts_wrong_new_parent_kind(self):
 
766
        tt, root = self.get_transform()
 
767
        parent_id = tt.new_directory('parent', root, 'parent-id')
 
768
        tt.new_file('child,', parent_id, 'contents2', 'file-id')
 
769
        tt.apply()
 
770
        tt, root = self.get_transform()
 
771
        parent_id = tt.trans_id_file_id('parent-id')
 
772
        tt.delete_contents(parent_id)
 
773
        tt.create_file('contents', parent_id)
 
774
        raw_conflicts = resolve_conflicts(tt)
 
775
        self.assertEqual(set([('non-directory parent', 'Created directory',
 
776
                         'new-3')]), raw_conflicts)
 
777
        tt.apply()
 
778
        self.assertEqual(None, self.wt.path2id('parent'))
 
779
        self.assertEqual('parent-id', self.wt.path2id('parent.new'))
 
780
 
 
781
    def test_resolve_conflicts_wrong_parent_kind_unversioned(self):
 
782
        tt, root = self.get_transform()
 
783
        parent_id = tt.new_directory('parent', root)
 
784
        tt.new_file('child,', parent_id, 'contents2')
 
785
        tt.apply()
 
786
        tt, root = self.get_transform()
 
787
        parent_id = tt.trans_id_tree_path('parent')
 
788
        tt.delete_contents(parent_id)
 
789
        tt.create_file('contents', parent_id)
 
790
        resolve_conflicts(tt)
 
791
        tt.apply()
 
792
        self.assertIs(None, self.wt.path2id('parent'))
 
793
        self.assertIs(None, self.wt.path2id('parent.new'))
625
794
 
626
795
    def test_moving_versioned_directories(self):
627
796
        create, root = self.get_transform()
643
812
        create.apply()
644
813
        transform, root = self.get_transform()
645
814
        transform.adjust_root_path('oldroot', fun)
646
 
        new_root=transform.trans_id_tree_path('')
 
815
        new_root = transform.trans_id_tree_path('')
647
816
        transform.version_file('new-root', new_root)
648
817
        transform.apply()
649
818
 
661
830
        rename.set_executability(True, myfile)
662
831
        rename.apply()
663
832
 
664
 
    def test_find_interesting(self):
665
 
        create, root = self.get_transform()
666
 
        wt = create._tree
667
 
        create.new_file('vfile', root, 'myfile-text', 'myfile-id')
668
 
        create.new_file('uvfile', root, 'othertext')
669
 
        create.apply()
670
 
        result = self.applyDeprecated(symbol_versioning.zero_fifteen,
671
 
            find_interesting, wt, wt, ['vfile'])
672
 
        self.assertEqual(result, set(['myfile-id']))
673
 
 
674
833
    def test_set_executability_order(self):
675
834
        """Ensure that executability behaves the same, no matter what order.
676
 
        
 
835
 
677
836
        - create file and set executability simultaneously
678
837
        - create file and set executability afterward
679
838
        - unsetting the executability of a file whose executability has not been
703
862
        transform, root = self.get_transform()
704
863
        transform.new_file('file1', root, 'contents', 'file1-id', True)
705
864
        transform.apply()
 
865
        self.wt.lock_write()
 
866
        self.addCleanup(self.wt.unlock)
706
867
        self.assertTrue(self.wt.is_executable('file1-id'))
707
868
        transform, root = self.get_transform()
708
869
        file1_id = transform.trans_id_tree_file_id('file1-id')
748
909
        transform.apply()
749
910
        transform, root = self.get_transform()
750
911
        try:
751
 
            self.assertEqual([], list(transform._iter_changes()))
 
912
            self.assertEqual([], list(transform.iter_changes()))
752
913
            old = transform.trans_id_tree_file_id('id-1')
753
914
            transform.unversion_file(old)
754
915
            self.assertEqual([('id-1', ('old', None), False, (True, False),
755
916
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
756
 
                (True, True))], list(transform._iter_changes()))
 
917
                (True, True))], list(transform.iter_changes()))
757
918
            transform.new_directory('new', root, 'id-1')
758
919
            self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
759
920
                ('eert_toor', 'eert_toor'), ('old', 'new'),
760
921
                ('file', 'directory'),
761
 
                (True, False))], list(transform._iter_changes()))
 
922
                (True, False))], list(transform.iter_changes()))
762
923
        finally:
763
924
            transform.finalize()
764
925
 
773
934
            transform.version_file('id-1', old)
774
935
            self.assertEqual([('id-1', (None, 'old'), False, (False, True),
775
936
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
776
 
                (False, False))], list(transform._iter_changes()))
 
937
                (False, False))], list(transform.iter_changes()))
777
938
        finally:
778
939
            transform.finalize()
779
940
 
789
950
            old = transform.trans_id_tree_path('old')
790
951
            subdir = transform.trans_id_tree_file_id('subdir-id')
791
952
            new = transform.trans_id_tree_path('new')
792
 
            self.assertEqual([], list(transform._iter_changes()))
 
953
            self.assertEqual([], list(transform.iter_changes()))
793
954
 
794
955
            #content deletion
795
956
            transform.delete_contents(old)
796
957
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
797
958
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
798
 
                (False, False))], list(transform._iter_changes()))
 
959
                (False, False))], list(transform.iter_changes()))
799
960
 
800
961
            #content change
801
962
            transform.create_file('blah', old)
802
963
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
803
964
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
804
 
                (False, False))], list(transform._iter_changes()))
 
965
                (False, False))], list(transform.iter_changes()))
805
966
            transform.cancel_deletion(old)
806
967
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
807
968
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
808
 
                (False, False))], list(transform._iter_changes()))
 
969
                (False, False))], list(transform.iter_changes()))
809
970
            transform.cancel_creation(old)
810
971
 
811
972
            # move file_id to a different file
812
 
            self.assertEqual([], list(transform._iter_changes()))
 
973
            self.assertEqual([], list(transform.iter_changes()))
813
974
            transform.unversion_file(old)
814
975
            transform.version_file('id-1', new)
815
976
            transform.adjust_path('old', root, new)
816
977
            self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
817
978
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
818
 
                (False, False))], list(transform._iter_changes()))
 
979
                (False, False))], list(transform.iter_changes()))
819
980
            transform.cancel_versioning(new)
820
981
            transform._removed_id = set()
821
982
 
822
983
            #execute bit
823
 
            self.assertEqual([], list(transform._iter_changes()))
 
984
            self.assertEqual([], list(transform.iter_changes()))
824
985
            transform.set_executability(True, old)
825
986
            self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
826
987
                ('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
827
 
                (False, True))], list(transform._iter_changes()))
 
988
                (False, True))], list(transform.iter_changes()))
828
989
            transform.set_executability(None, old)
829
990
 
830
991
            # filename
831
 
            self.assertEqual([], list(transform._iter_changes()))
 
992
            self.assertEqual([], list(transform.iter_changes()))
832
993
            transform.adjust_path('new', root, old)
833
994
            transform._new_parent = {}
834
995
            self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
835
996
                ('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
836
 
                (False, False))], list(transform._iter_changes()))
 
997
                (False, False))], list(transform.iter_changes()))
837
998
            transform._new_name = {}
838
999
 
839
1000
            # parent directory
840
 
            self.assertEqual([], list(transform._iter_changes()))
 
1001
            self.assertEqual([], list(transform.iter_changes()))
841
1002
            transform.adjust_path('new', subdir, old)
842
1003
            transform._new_name = {}
843
1004
            self.assertEqual([('id-1', ('old', 'subdir/old'), False,
844
1005
                (True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
845
1006
                ('file', 'file'), (False, False))],
846
 
                list(transform._iter_changes()))
 
1007
                list(transform.iter_changes()))
847
1008
            transform._new_path = {}
848
1009
 
849
1010
        finally:
871
1032
                ('id-2', (u'file2', u'file2'), False, (True, True),
872
1033
                ('eert_toor', 'eert_toor'), ('file2', u'file2'),
873
1034
                ('file', 'file'), (False, True))],
874
 
                list(transform._iter_changes()))
 
1035
                list(transform.iter_changes()))
875
1036
        finally:
876
1037
            transform.finalize()
877
1038
 
891
1052
            transform.adjust_path('flitter', root, floater)
892
1053
            self.assertEqual([('floater-id', ('floater', 'flitter'), False,
893
1054
            (True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
894
 
            (None, None), (False, False))], list(transform._iter_changes()))
 
1055
            (None, None), (False, False))], list(transform.iter_changes()))
895
1056
        finally:
896
1057
            transform.finalize()
897
1058
 
906
1067
        try:
907
1068
            old = transform.trans_id_tree_path('old')
908
1069
            subdir = transform.trans_id_tree_file_id('subdir-id')
909
 
            self.assertEqual([], list(transform._iter_changes()))
 
1070
            self.assertEqual([], list(transform.iter_changes()))
910
1071
            transform.delete_contents(subdir)
911
1072
            transform.create_directory(subdir)
912
1073
            transform.set_executability(False, old)
913
1074
            transform.unversion_file(old)
914
1075
            transform.version_file('id-1', old)
915
1076
            transform.adjust_path('old', root, old)
916
 
            self.assertEqual([], list(transform._iter_changes()))
 
1077
            self.assertEqual([], list(transform.iter_changes()))
917
1078
        finally:
918
1079
            transform.finalize()
919
1080
 
1084
1245
        transform.cancel_creation(parent)
1085
1246
        transform.finalize()
1086
1247
 
1087
 
    def test_change_entry(self):
1088
 
        txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
1089
 
        self.callDeprecated([txt], change_entry, None, None, None, None, None,
1090
 
            None, None, None)
1091
 
 
1092
 
    def test_case_insensitive_clash(self):
1093
 
        self.requireFeature(CaseInsensitiveFilesystemFeature)
 
1248
    def test_rollback_on_directory_clash(self):
1094
1249
        def tt_helper():
1095
1250
            wt = self.make_branch_and_tree('.')
1096
1251
            tt = TreeTransform(wt)  # TreeTransform obtains write lock
1097
1252
            try:
1098
 
                tt.new_file('foo', tt.root, 'bar')
1099
 
                tt.new_file('Foo', tt.root, 'spam')
 
1253
                foo = tt.new_directory('foo', tt.root)
 
1254
                tt.new_file('bar', foo, 'foobar')
 
1255
                baz = tt.new_directory('baz', tt.root)
 
1256
                tt.new_file('qux', baz, 'quux')
 
1257
                # Ask for a rename 'foo' -> 'baz'
 
1258
                tt.adjust_path('baz', tt.root, foo)
1100
1259
                # Lie to tt that we've already resolved all conflicts.
1101
1260
                tt.apply(no_conflicts=True)
1102
1261
            except:
1103
1262
                wt.unlock()
1104
1263
                raise
 
1264
        # The rename will fail because the target directory is not empty (but
 
1265
        # raises FileExists anyway).
1105
1266
        err = self.assertRaises(errors.FileExists, tt_helper)
1106
1267
        self.assertContainsRe(str(err),
1107
 
            "^File exists: .+/foo")
 
1268
            "^File exists: .+/baz")
1108
1269
 
1109
1270
    def test_two_directories_clash(self):
1110
1271
        def tt_helper():
1113
1274
            try:
1114
1275
                foo_1 = tt.new_directory('foo', tt.root)
1115
1276
                tt.new_directory('bar', foo_1)
 
1277
                # Adding the same directory with a different content
1116
1278
                foo_2 = tt.new_directory('foo', tt.root)
1117
1279
                tt.new_directory('baz', foo_2)
1118
1280
                # Lie to tt that we've already resolved all conflicts.
1131
1293
            try:
1132
1294
                foo_1 = tt.new_directory('foo', tt.root)
1133
1295
                tt.new_directory('bar', foo_1)
 
1296
                # Adding the same directory with a different content
1134
1297
                foo_2 = tt.new_directory('foo', tt.root)
1135
1298
                tt.new_directory('baz', foo_2)
1136
1299
                # Lie to tt that we've already resolved all conflicts.
1142
1305
        self.assertContainsRe(str(err),
1143
1306
            "^File exists: .+/foo")
1144
1307
 
 
1308
    def test_file_to_directory(self):
 
1309
        wt = self.make_branch_and_tree('.')
 
1310
        self.build_tree(['foo'])
 
1311
        wt.add(['foo'])
 
1312
        wt.commit("one")
 
1313
        tt = TreeTransform(wt)
 
1314
        self.addCleanup(tt.finalize)
 
1315
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1316
        tt.delete_contents(foo_trans_id)
 
1317
        tt.create_directory(foo_trans_id)
 
1318
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1319
        tt.create_file(["aa\n"], bar_trans_id)
 
1320
        tt.version_file("bar-1", bar_trans_id)
 
1321
        tt.apply()
 
1322
        self.failUnlessExists("foo/bar")
 
1323
        wt.lock_read()
 
1324
        try:
 
1325
            self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1326
                    "directory")
 
1327
        finally:
 
1328
            wt.unlock()
 
1329
        wt.commit("two")
 
1330
        changes = wt.changes_from(wt.basis_tree())
 
1331
        self.assertFalse(changes.has_changed(), changes)
 
1332
 
 
1333
    def test_file_to_symlink(self):
 
1334
        self.requireFeature(SymlinkFeature)
 
1335
        wt = self.make_branch_and_tree('.')
 
1336
        self.build_tree(['foo'])
 
1337
        wt.add(['foo'])
 
1338
        wt.commit("one")
 
1339
        tt = TreeTransform(wt)
 
1340
        self.addCleanup(tt.finalize)
 
1341
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1342
        tt.delete_contents(foo_trans_id)
 
1343
        tt.create_symlink("bar", foo_trans_id)
 
1344
        tt.apply()
 
1345
        self.failUnlessExists("foo")
 
1346
        wt.lock_read()
 
1347
        self.addCleanup(wt.unlock)
 
1348
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1349
                "symlink")
 
1350
 
 
1351
    def test_dir_to_file(self):
 
1352
        wt = self.make_branch_and_tree('.')
 
1353
        self.build_tree(['foo/', 'foo/bar'])
 
1354
        wt.add(['foo', 'foo/bar'])
 
1355
        wt.commit("one")
 
1356
        tt = TreeTransform(wt)
 
1357
        self.addCleanup(tt.finalize)
 
1358
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1359
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1360
        tt.delete_contents(foo_trans_id)
 
1361
        tt.delete_versioned(bar_trans_id)
 
1362
        tt.create_file(["aa\n"], foo_trans_id)
 
1363
        tt.apply()
 
1364
        self.failUnlessExists("foo")
 
1365
        wt.lock_read()
 
1366
        self.addCleanup(wt.unlock)
 
1367
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1368
                "file")
 
1369
 
 
1370
    def test_dir_to_hardlink(self):
 
1371
        self.requireFeature(HardlinkFeature)
 
1372
        wt = self.make_branch_and_tree('.')
 
1373
        self.build_tree(['foo/', 'foo/bar'])
 
1374
        wt.add(['foo', 'foo/bar'])
 
1375
        wt.commit("one")
 
1376
        tt = TreeTransform(wt)
 
1377
        self.addCleanup(tt.finalize)
 
1378
        foo_trans_id = tt.trans_id_tree_path("foo")
 
1379
        bar_trans_id = tt.trans_id_tree_path("foo/bar")
 
1380
        tt.delete_contents(foo_trans_id)
 
1381
        tt.delete_versioned(bar_trans_id)
 
1382
        self.build_tree(['baz'])
 
1383
        tt.create_hardlink("baz", foo_trans_id)
 
1384
        tt.apply()
 
1385
        self.failUnlessExists("foo")
 
1386
        self.failUnlessExists("baz")
 
1387
        wt.lock_read()
 
1388
        self.addCleanup(wt.unlock)
 
1389
        self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
 
1390
                "file")
 
1391
 
 
1392
    def test_no_final_path(self):
 
1393
        transform, root = self.get_transform()
 
1394
        trans_id = transform.trans_id_file_id('foo')
 
1395
        transform.create_file('bar', trans_id)
 
1396
        transform.cancel_creation(trans_id)
 
1397
        transform.apply()
 
1398
 
 
1399
    def test_create_from_tree(self):
 
1400
        tree1 = self.make_branch_and_tree('tree1')
 
1401
        self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
 
1402
        tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
 
1403
        tree2 = self.make_branch_and_tree('tree2')
 
1404
        tt = TreeTransform(tree2)
 
1405
        foo_trans_id = tt.create_path('foo', tt.root)
 
1406
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1407
        bar_trans_id = tt.create_path('bar', tt.root)
 
1408
        create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
 
1409
        tt.apply()
 
1410
        self.assertEqual('directory', osutils.file_kind('tree2/foo'))
 
1411
        self.assertFileEqual('baz', 'tree2/bar')
 
1412
 
 
1413
    def test_create_from_tree_bytes(self):
 
1414
        """Provided lines are used instead of tree content."""
 
1415
        tree1 = self.make_branch_and_tree('tree1')
 
1416
        self.build_tree_contents([('tree1/foo', 'bar'),])
 
1417
        tree1.add('foo', 'foo-id')
 
1418
        tree2 = self.make_branch_and_tree('tree2')
 
1419
        tt = TreeTransform(tree2)
 
1420
        foo_trans_id = tt.create_path('foo', tt.root)
 
1421
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
 
1422
        tt.apply()
 
1423
        self.assertFileEqual('qux', 'tree2/foo')
 
1424
 
 
1425
    def test_create_from_tree_symlink(self):
 
1426
        self.requireFeature(SymlinkFeature)
 
1427
        tree1 = self.make_branch_and_tree('tree1')
 
1428
        os.symlink('bar', 'tree1/foo')
 
1429
        tree1.add('foo', 'foo-id')
 
1430
        tt = TreeTransform(self.make_branch_and_tree('tree2'))
 
1431
        foo_trans_id = tt.create_path('foo', tt.root)
 
1432
        create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
 
1433
        tt.apply()
 
1434
        self.assertEqual('bar', os.readlink('tree2/foo'))
 
1435
 
1145
1436
 
1146
1437
class TransformGroup(object):
1147
1438
 
1161
1452
 
1162
1453
 
1163
1454
class TestTransformMerge(TestCaseInTempDir):
 
1455
 
1164
1456
    def test_text_merge(self):
1165
1457
        root_id = generate_ids.gen_root_id()
1166
1458
        base = TransformGroup("base", root_id)
1196
1488
        this.tt.new_file('i', this.root, '1\n2\n3\n4\n', 'i')
1197
1489
        this.tt.apply()
1198
1490
        Merge3Merger(this.wt, this.wt, base.wt, other.wt)
 
1491
 
1199
1492
        # textual merge
1200
1493
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1201
1494
        # three-way text conflict
1202
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1495
        self.assertEqual(this.wt.get_file('b').read(),
1203
1496
                         conflict_text('b', 'b2'))
1204
1497
        # OTHER wins
1205
1498
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1209
1502
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1210
1503
        # No change
1211
1504
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1212
 
        # Correct correct results when THIS == OTHER 
 
1505
        # Correct correct results when THIS == OTHER
1213
1506
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1214
1507
        # Text conflict when THIS & OTHER are text and BASE is dir
1215
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1508
        self.assertEqual(this.wt.get_file('h').read(),
1216
1509
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1217
1510
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1218
1511
                         '1\n2\n3\n4\n')
1219
1512
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1220
1513
                         'h\ni\nj\nk\n')
1221
1514
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1222
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1515
        self.assertEqual(this.wt.get_file('i').read(),
1223
1516
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1224
1517
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1225
1518
                         '1\n2\n3\n4\n')
1249
1542
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1250
1543
            tg.tt.new_file('c', tg.root, 'c', 'c')
1251
1544
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1252
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1253
 
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'), 
 
1545
        targets = ((base, 'base-e', 'base-f', None, None),
 
1546
                   (this, 'other-e', 'this-f', 'other-g', 'this-h'),
1254
1547
                   (other, 'other-e', None, 'other-g', 'other-h'))
1255
1548
        for tg, e_target, f_target, g_target, h_target in targets:
1256
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1549
            for link, target in (('e', e_target), ('f', f_target),
1257
1550
                                 ('g', g_target), ('h', h_target)):
1258
1551
                if target is not None:
1259
1552
                    tg.tt.new_symlink(link, tg.root, target, link)
1285
1578
        base = TransformGroup("BASE", root_id)
1286
1579
        this = TransformGroup("THIS", root_id)
1287
1580
        other = TransformGroup("OTHER", root_id)
1288
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1581
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1289
1582
                                   for t in [base, this, other]]
1290
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1583
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1291
1584
                                   for t in [base, this, other]]
1292
1585
        base.tt.new_directory('c', base_a, 'c')
1293
1586
        this.tt.new_directory('c1', this_a, 'c')
1318
1611
        base = TransformGroup("BASE", root_id)
1319
1612
        this = TransformGroup("THIS", root_id)
1320
1613
        other = TransformGroup("OTHER", root_id)
1321
 
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a') 
 
1614
        base_a, this_a, other_a = [t.tt.new_directory('a', t.root, 'a')
1322
1615
                                   for t in [base, this, other]]
1323
 
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b') 
 
1616
        base_b, this_b, other_b = [t.tt.new_directory('b', t.root, 'b')
1324
1617
                                   for t in [base, this, other]]
1325
1618
 
1326
1619
        base.tt.new_file('g', base_a, 'g', 'g')
1417
1710
        os.symlink('foo', 'target2/symlink')
1418
1711
        build_tree(source.basis_tree(), target)
1419
1712
        self.assertEqual([], target.conflicts())
1420
 
        
 
1713
 
1421
1714
    def test_directory_conflict_handling(self):
1422
1715
        """Ensure that when building trees, conflict handling is done"""
1423
1716
        source = self.make_branch_and_tree('source')
1479
1772
        target = self.make_branch_and_tree('target')
1480
1773
        self.build_tree(['target/name'])
1481
1774
        target.add('name')
1482
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1775
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1483
1776
            build_tree, source.basis_tree(), target)
1484
1777
 
1485
1778
    def test_build_tree_rename_count(self):
1499
1792
        # children of non-root directories should not be renamed
1500
1793
        self.assertEqual(2, transform_result.rename_count)
1501
1794
 
 
1795
    def create_ab_tree(self):
 
1796
        """Create a committed test tree with two files"""
 
1797
        source = self.make_branch_and_tree('source')
 
1798
        self.build_tree_contents([('source/file1', 'A')])
 
1799
        self.build_tree_contents([('source/file2', 'B')])
 
1800
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
 
1801
        source.commit('commit files')
 
1802
        source.lock_write()
 
1803
        self.addCleanup(source.unlock)
 
1804
        return source
 
1805
 
1502
1806
    def test_build_tree_accelerator_tree(self):
1503
 
        source = self.make_branch_and_tree('source')
1504
 
        self.build_tree_contents([('source/file1', 'A')])
1505
 
        self.build_tree_contents([('source/file2', 'B')])
1506
 
        source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1507
 
        source.commit('commit files')
 
1807
        source = self.create_ab_tree()
1508
1808
        self.build_tree_contents([('source/file2', 'C')])
1509
1809
        calls = []
1510
1810
        real_source_get_file = source.get_file
1512
1812
            calls.append(file_id)
1513
1813
            return real_source_get_file(file_id, path)
1514
1814
        source.get_file = get_file
1515
 
        source.lock_read()
1516
 
        self.addCleanup(source.unlock)
1517
1815
        target = self.make_branch_and_tree('target')
1518
1816
        revision_tree = source.basis_tree()
1519
1817
        revision_tree.lock_read()
1522
1820
        self.assertEqual(['file1-id'], calls)
1523
1821
        target.lock_read()
1524
1822
        self.addCleanup(target.unlock)
1525
 
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1823
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1526
1824
 
1527
1825
    def test_build_tree_accelerator_tree_missing_file(self):
1528
 
        source = self.make_branch_and_tree('source')
1529
 
        self.build_tree_contents([('source/file1', 'A')])
1530
 
        self.build_tree_contents([('source/file2', 'B')])
1531
 
        source.add(['file1', 'file2'])
1532
 
        source.commit('commit files')
 
1826
        source = self.create_ab_tree()
1533
1827
        os.unlink('source/file1')
1534
1828
        source.remove(['file2'])
1535
1829
        target = self.make_branch_and_tree('target')
1539
1833
        build_tree(revision_tree, target, source)
1540
1834
        target.lock_read()
1541
1835
        self.addCleanup(target.unlock)
1542
 
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1836
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1543
1837
 
1544
1838
    def test_build_tree_accelerator_wrong_kind(self):
 
1839
        self.requireFeature(SymlinkFeature)
1545
1840
        source = self.make_branch_and_tree('source')
1546
1841
        self.build_tree_contents([('source/file1', '')])
1547
1842
        self.build_tree_contents([('source/file2', '')])
1557
1852
            calls.append(file_id)
1558
1853
            return real_source_get_file(file_id, path)
1559
1854
        source.get_file = get_file
1560
 
        source.lock_read()
1561
 
        self.addCleanup(source.unlock)
1562
1855
        target = self.make_branch_and_tree('target')
1563
1856
        revision_tree = source.basis_tree()
1564
1857
        revision_tree.lock_read()
1567
1860
        self.assertEqual([], calls)
1568
1861
        target.lock_read()
1569
1862
        self.addCleanup(target.unlock)
1570
 
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1863
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1864
 
 
1865
    def test_build_tree_hardlink(self):
 
1866
        self.requireFeature(HardlinkFeature)
 
1867
        source = self.create_ab_tree()
 
1868
        target = self.make_branch_and_tree('target')
 
1869
        revision_tree = source.basis_tree()
 
1870
        revision_tree.lock_read()
 
1871
        self.addCleanup(revision_tree.unlock)
 
1872
        build_tree(revision_tree, target, source, hardlink=True)
 
1873
        target.lock_read()
 
1874
        self.addCleanup(target.unlock)
 
1875
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1876
        source_stat = os.stat('source/file1')
 
1877
        target_stat = os.stat('target/file1')
 
1878
        self.assertEqual(source_stat, target_stat)
 
1879
 
 
1880
        # Explicitly disallowing hardlinks should prevent them.
 
1881
        target2 = self.make_branch_and_tree('target2')
 
1882
        build_tree(revision_tree, target2, source, hardlink=False)
 
1883
        target2.lock_read()
 
1884
        self.addCleanup(target2.unlock)
 
1885
        self.assertEqual([], list(target2.iter_changes(revision_tree)))
 
1886
        source_stat = os.stat('source/file1')
 
1887
        target2_stat = os.stat('target2/file1')
 
1888
        self.assertNotEqual(source_stat, target2_stat)
1571
1889
 
1572
1890
    def test_build_tree_accelerator_tree_moved(self):
1573
1891
        source = self.make_branch_and_tree('source')
1584
1902
        build_tree(revision_tree, target, source)
1585
1903
        target.lock_read()
1586
1904
        self.addCleanup(target.unlock)
1587
 
        self.assertEqual([], list(target._iter_changes(revision_tree)))
 
1905
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1906
 
 
1907
    def test_build_tree_hardlinks_preserve_execute(self):
 
1908
        self.requireFeature(HardlinkFeature)
 
1909
        source = self.create_ab_tree()
 
1910
        tt = TreeTransform(source)
 
1911
        trans_id = tt.trans_id_tree_file_id('file1-id')
 
1912
        tt.set_executability(True, trans_id)
 
1913
        tt.apply()
 
1914
        self.assertTrue(source.is_executable('file1-id'))
 
1915
        target = self.make_branch_and_tree('target')
 
1916
        revision_tree = source.basis_tree()
 
1917
        revision_tree.lock_read()
 
1918
        self.addCleanup(revision_tree.unlock)
 
1919
        build_tree(revision_tree, target, source, hardlink=True)
 
1920
        target.lock_read()
 
1921
        self.addCleanup(target.unlock)
 
1922
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1923
        self.assertTrue(source.is_executable('file1-id'))
 
1924
 
 
1925
    def install_rot13_content_filter(self, pattern):
 
1926
        # We could use
 
1927
        # self.addCleanup(filters._reset_registry, filters._reset_registry())
 
1928
        # below, but that looks a bit... hard to read even if it's exactly
 
1929
        # the same thing.
 
1930
        original_registry = filters._reset_registry()
 
1931
        def restore_registry():
 
1932
            filters._reset_registry(original_registry)
 
1933
        self.addCleanup(restore_registry)
 
1934
        def rot13(chunks, context=None):
 
1935
            return [''.join(chunks).encode('rot13')]
 
1936
        rot13filter = filters.ContentFilter(rot13, rot13)
 
1937
        filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
 
1938
        os.mkdir(self.test_home_dir + '/.bazaar')
 
1939
        rules_filename = self.test_home_dir + '/.bazaar/rules'
 
1940
        f = open(rules_filename, 'wb')
 
1941
        f.write('[name %s]\nrot13=yes\n' % (pattern,))
 
1942
        f.close()
 
1943
        def uninstall_rules():
 
1944
            os.remove(rules_filename)
 
1945
            rules.reset_rules()
 
1946
        self.addCleanup(uninstall_rules)
 
1947
        rules.reset_rules()
 
1948
 
 
1949
    def test_build_tree_content_filtered_files_are_not_hardlinked(self):
 
1950
        """build_tree will not hardlink files that have content filtering rules
 
1951
        applied to them (but will still hardlink other files from the same tree
 
1952
        if it can).
 
1953
        """
 
1954
        self.requireFeature(HardlinkFeature)
 
1955
        self.install_rot13_content_filter('file1')
 
1956
        source = self.create_ab_tree()
 
1957
        target = self.make_branch_and_tree('target')
 
1958
        revision_tree = source.basis_tree()
 
1959
        revision_tree.lock_read()
 
1960
        self.addCleanup(revision_tree.unlock)
 
1961
        build_tree(revision_tree, target, source, hardlink=True)
 
1962
        target.lock_read()
 
1963
        self.addCleanup(target.unlock)
 
1964
        self.assertEqual([], list(target.iter_changes(revision_tree)))
 
1965
        source_stat = os.stat('source/file1')
 
1966
        target_stat = os.stat('target/file1')
 
1967
        self.assertNotEqual(source_stat, target_stat)
 
1968
        source_stat = os.stat('source/file2')
 
1969
        target_stat = os.stat('target/file2')
 
1970
        self.assertEqualStat(source_stat, target_stat)
 
1971
 
 
1972
    def test_case_insensitive_build_tree_inventory(self):
 
1973
        if (tests.CaseInsensitiveFilesystemFeature.available()
 
1974
            or tests.CaseInsCasePresFilenameFeature.available()):
 
1975
            raise tests.UnavailableFeature('Fully case sensitive filesystem')
 
1976
        source = self.make_branch_and_tree('source')
 
1977
        self.build_tree(['source/file', 'source/FILE'])
 
1978
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
 
1979
        source.commit('added files')
 
1980
        # Don't try this at home, kids!
 
1981
        # Force the tree to report that it is case insensitive
 
1982
        target = self.make_branch_and_tree('target')
 
1983
        target.case_sensitive = False
 
1984
        build_tree(source.basis_tree(), target, source, delta_from_tree=True)
 
1985
        self.assertEqual('file.moved', target.id2path('lower-id'))
 
1986
        self.assertEqual('FILE', target.id2path('upper-id'))
 
1987
 
 
1988
 
 
1989
class TestCommitTransform(tests.TestCaseWithTransport):
 
1990
 
 
1991
    def get_branch(self):
 
1992
        tree = self.make_branch_and_tree('tree')
 
1993
        tree.lock_write()
 
1994
        self.addCleanup(tree.unlock)
 
1995
        tree.commit('empty commit')
 
1996
        return tree.branch
 
1997
 
 
1998
    def get_branch_and_transform(self):
 
1999
        branch = self.get_branch()
 
2000
        tt = TransformPreview(branch.basis_tree())
 
2001
        self.addCleanup(tt.finalize)
 
2002
        return branch, tt
 
2003
 
 
2004
    def test_commit_wrong_basis(self):
 
2005
        branch = self.get_branch()
 
2006
        basis = branch.repository.revision_tree(
 
2007
            _mod_revision.NULL_REVISION)
 
2008
        tt = TransformPreview(basis)
 
2009
        self.addCleanup(tt.finalize)
 
2010
        e = self.assertRaises(ValueError, tt.commit, branch, '')
 
2011
        self.assertEqual('TreeTransform not based on branch basis: null:',
 
2012
                         str(e))
 
2013
 
 
2014
    def test_empy_commit(self):
 
2015
        branch, tt = self.get_branch_and_transform()
 
2016
        rev = tt.commit(branch, 'my message')
 
2017
        self.assertEqual(2, branch.revno())
 
2018
        repo = branch.repository
 
2019
        self.assertEqual('my message', repo.get_revision(rev).message)
 
2020
 
 
2021
    def test_merge_parents(self):
 
2022
        branch, tt = self.get_branch_and_transform()
 
2023
        rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
 
2024
        self.assertEqual(['rev1b', 'rev1c'],
 
2025
                         branch.basis_tree().get_parent_ids()[1:])
 
2026
 
 
2027
    def test_first_commit(self):
 
2028
        branch = self.make_branch('branch')
 
2029
        branch.lock_write()
 
2030
        self.addCleanup(branch.unlock)
 
2031
        tt = TransformPreview(branch.basis_tree())
 
2032
        self.addCleanup(tt.finalize)
 
2033
        tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
 
2034
        rev = tt.commit(branch, 'my message')
 
2035
        self.assertEqual([], branch.basis_tree().get_parent_ids())
 
2036
        self.assertNotEqual(_mod_revision.NULL_REVISION,
 
2037
                            branch.last_revision())
 
2038
 
 
2039
    def test_first_commit_with_merge_parents(self):
 
2040
        branch = self.make_branch('branch')
 
2041
        branch.lock_write()
 
2042
        self.addCleanup(branch.unlock)
 
2043
        tt = TransformPreview(branch.basis_tree())
 
2044
        self.addCleanup(tt.finalize)
 
2045
        e = self.assertRaises(ValueError, tt.commit, branch,
 
2046
                          'my message', ['rev1b-id'])
 
2047
        self.assertEqual('Cannot supply merge parents for first commit.',
 
2048
                         str(e))
 
2049
        self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
 
2050
 
 
2051
    def test_add_files(self):
 
2052
        branch, tt = self.get_branch_and_transform()
 
2053
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2054
        trans_id = tt.new_directory('dir', tt.root, 'dir-id')
 
2055
        if SymlinkFeature.available():
 
2056
            tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
 
2057
        rev = tt.commit(branch, 'message')
 
2058
        tree = branch.basis_tree()
 
2059
        self.assertEqual('file', tree.id2path('file-id'))
 
2060
        self.assertEqual('contents', tree.get_file_text('file-id'))
 
2061
        self.assertEqual('dir', tree.id2path('dir-id'))
 
2062
        if SymlinkFeature.available():
 
2063
            self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
 
2064
            self.assertEqual('target', tree.get_symlink_target('symlink-id'))
 
2065
 
 
2066
    def test_add_unversioned(self):
 
2067
        branch, tt = self.get_branch_and_transform()
 
2068
        tt.new_file('file', tt.root, 'contents')
 
2069
        self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
 
2070
                          'message', strict=True)
 
2071
 
 
2072
    def test_modify_strict(self):
 
2073
        branch, tt = self.get_branch_and_transform()
 
2074
        tt.new_file('file', tt.root, 'contents', 'file-id')
 
2075
        tt.commit(branch, 'message', strict=True)
 
2076
        tt = TransformPreview(branch.basis_tree())
 
2077
        self.addCleanup(tt.finalize)
 
2078
        trans_id = tt.trans_id_file_id('file-id')
 
2079
        tt.delete_contents(trans_id)
 
2080
        tt.create_file('contents', trans_id)
 
2081
        tt.commit(branch, 'message', strict=True)
 
2082
 
 
2083
    def test_commit_malformed(self):
 
2084
        """Committing a malformed transform should raise an exception.
 
2085
 
 
2086
        In this case, we are adding a file without adding its parent.
 
2087
        """
 
2088
        branch, tt = self.get_branch_and_transform()
 
2089
        parent_id = tt.trans_id_file_id('parent-id')
 
2090
        tt.new_file('file', parent_id, 'contents', 'file-id')
 
2091
        self.assertRaises(errors.MalformedTransform, tt.commit, branch,
 
2092
                          'message')
1588
2093
 
1589
2094
 
1590
2095
class MockTransform(object):
1745
2250
        parent = tt.trans_id_file_id('parent-id')
1746
2251
        tt.new_file('file', parent, 'Contents')
1747
2252
        resolve_conflicts(tt)
 
2253
 
 
2254
 
 
2255
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
 
2256
                  ('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
 
2257
                  (False, False))
 
2258
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
 
2259
              ('', ''), ('directory', 'directory'), (False, None))
 
2260
 
 
2261
 
 
2262
class TestTransformPreview(tests.TestCaseWithTransport):
 
2263
 
 
2264
    def create_tree(self):
 
2265
        tree = self.make_branch_and_tree('.')
 
2266
        self.build_tree_contents([('a', 'content 1')])
 
2267
        tree.set_root_id('TREE_ROOT')
 
2268
        tree.add('a', 'a-id')
 
2269
        tree.commit('rev1', rev_id='rev1')
 
2270
        return tree.branch.repository.revision_tree('rev1')
 
2271
 
 
2272
    def get_empty_preview(self):
 
2273
        repository = self.make_repository('repo')
 
2274
        tree = repository.revision_tree(_mod_revision.NULL_REVISION)
 
2275
        preview = TransformPreview(tree)
 
2276
        self.addCleanup(preview.finalize)
 
2277
        return preview
 
2278
 
 
2279
    def test_transform_preview(self):
 
2280
        revision_tree = self.create_tree()
 
2281
        preview = TransformPreview(revision_tree)
 
2282
        self.addCleanup(preview.finalize)
 
2283
 
 
2284
    def test_transform_preview_tree(self):
 
2285
        revision_tree = self.create_tree()
 
2286
        preview = TransformPreview(revision_tree)
 
2287
        self.addCleanup(preview.finalize)
 
2288
        preview.get_preview_tree()
 
2289
 
 
2290
    def test_transform_new_file(self):
 
2291
        revision_tree = self.create_tree()
 
2292
        preview = TransformPreview(revision_tree)
 
2293
        self.addCleanup(preview.finalize)
 
2294
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2295
        preview_tree = preview.get_preview_tree()
 
2296
        self.assertEqual(preview_tree.kind('file2-id'), 'file')
 
2297
        self.assertEqual(
 
2298
            preview_tree.get_file('file2-id').read(), 'content B\n')
 
2299
 
 
2300
    def test_diff_preview_tree(self):
 
2301
        revision_tree = self.create_tree()
 
2302
        preview = TransformPreview(revision_tree)
 
2303
        self.addCleanup(preview.finalize)
 
2304
        preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
 
2305
        preview_tree = preview.get_preview_tree()
 
2306
        out = StringIO()
 
2307
        show_diff_trees(revision_tree, preview_tree, out)
 
2308
        lines = out.getvalue().splitlines()
 
2309
        self.assertEqual(lines[0], "=== added file 'file2'")
 
2310
        # 3 lines of diff administrivia
 
2311
        self.assertEqual(lines[4], "+content B")
 
2312
 
 
2313
    def test_transform_conflicts(self):
 
2314
        revision_tree = self.create_tree()
 
2315
        preview = TransformPreview(revision_tree)
 
2316
        self.addCleanup(preview.finalize)
 
2317
        preview.new_file('a', preview.root, 'content 2')
 
2318
        resolve_conflicts(preview)
 
2319
        trans_id = preview.trans_id_file_id('a-id')
 
2320
        self.assertEqual('a.moved', preview.final_name(trans_id))
 
2321
 
 
2322
    def get_tree_and_preview_tree(self):
 
2323
        revision_tree = self.create_tree()
 
2324
        preview = TransformPreview(revision_tree)
 
2325
        self.addCleanup(preview.finalize)
 
2326
        a_trans_id = preview.trans_id_file_id('a-id')
 
2327
        preview.delete_contents(a_trans_id)
 
2328
        preview.create_file('b content', a_trans_id)
 
2329
        preview_tree = preview.get_preview_tree()
 
2330
        return revision_tree, preview_tree
 
2331
 
 
2332
    def test_iter_changes(self):
 
2333
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2334
        root = revision_tree.inventory.root.file_id
 
2335
        self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
 
2336
                          (root, root), ('a', 'a'), ('file', 'file'),
 
2337
                          (False, False))],
 
2338
                          list(preview_tree.iter_changes(revision_tree)))
 
2339
 
 
2340
    def test_include_unchanged_succeeds(self):
 
2341
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2342
        changes = preview_tree.iter_changes(revision_tree,
 
2343
                                            include_unchanged=True)
 
2344
        root = revision_tree.inventory.root.file_id
 
2345
 
 
2346
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2347
 
 
2348
    def test_specific_files(self):
 
2349
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2350
        changes = preview_tree.iter_changes(revision_tree,
 
2351
                                            specific_files=[''])
 
2352
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2353
 
 
2354
    def test_want_unversioned(self):
 
2355
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2356
        changes = preview_tree.iter_changes(revision_tree,
 
2357
                                            want_unversioned=True)
 
2358
        self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
 
2359
 
 
2360
    def test_ignore_extra_trees_no_specific_files(self):
 
2361
        # extra_trees is harmless without specific_files, so we'll silently
 
2362
        # accept it, even though we won't use it.
 
2363
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2364
        preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
 
2365
 
 
2366
    def test_ignore_require_versioned_no_specific_files(self):
 
2367
        # require_versioned is meaningless without specific_files.
 
2368
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2369
        preview_tree.iter_changes(revision_tree, require_versioned=False)
 
2370
 
 
2371
    def test_ignore_pb(self):
 
2372
        # pb could be supported, but TT.iter_changes doesn't support it.
 
2373
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2374
        preview_tree.iter_changes(revision_tree)
 
2375
 
 
2376
    def test_kind(self):
 
2377
        revision_tree = self.create_tree()
 
2378
        preview = TransformPreview(revision_tree)
 
2379
        self.addCleanup(preview.finalize)
 
2380
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2381
        preview.new_directory('directory', preview.root, 'dir-id')
 
2382
        preview_tree = preview.get_preview_tree()
 
2383
        self.assertEqual('file', preview_tree.kind('file-id'))
 
2384
        self.assertEqual('directory', preview_tree.kind('dir-id'))
 
2385
 
 
2386
    def test_get_file_mtime(self):
 
2387
        preview = self.get_empty_preview()
 
2388
        file_trans_id = preview.new_file('file', preview.root, 'contents',
 
2389
                                         'file-id')
 
2390
        limbo_path = preview._limbo_name(file_trans_id)
 
2391
        preview_tree = preview.get_preview_tree()
 
2392
        self.assertEqual(os.stat(limbo_path).st_mtime,
 
2393
                         preview_tree.get_file_mtime('file-id'))
 
2394
 
 
2395
    def test_get_file_mtime_renamed(self):
 
2396
        work_tree = self.make_branch_and_tree('tree')
 
2397
        self.build_tree(['tree/file'])
 
2398
        work_tree.add('file', 'file-id')
 
2399
        preview = TransformPreview(work_tree)
 
2400
        self.addCleanup(preview.finalize)
 
2401
        file_trans_id = preview.trans_id_tree_file_id('file-id')
 
2402
        preview.adjust_path('renamed', preview.root, file_trans_id)
 
2403
        preview_tree = preview.get_preview_tree()
 
2404
        preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
 
2405
        work_mtime = work_tree.get_file_mtime('file-id', 'file')
 
2406
 
 
2407
    def test_get_file(self):
 
2408
        preview = self.get_empty_preview()
 
2409
        preview.new_file('file', preview.root, 'contents', 'file-id')
 
2410
        preview_tree = preview.get_preview_tree()
 
2411
        tree_file = preview_tree.get_file('file-id')
 
2412
        try:
 
2413
            self.assertEqual('contents', tree_file.read())
 
2414
        finally:
 
2415
            tree_file.close()
 
2416
 
 
2417
    def test_get_symlink_target(self):
 
2418
        self.requireFeature(SymlinkFeature)
 
2419
        preview = self.get_empty_preview()
 
2420
        preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
 
2421
        preview_tree = preview.get_preview_tree()
 
2422
        self.assertEqual('target',
 
2423
                         preview_tree.get_symlink_target('symlink-id'))
 
2424
 
 
2425
    def test_all_file_ids(self):
 
2426
        tree = self.make_branch_and_tree('tree')
 
2427
        self.build_tree(['tree/a', 'tree/b', 'tree/c'])
 
2428
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
 
2429
        preview = TransformPreview(tree)
 
2430
        self.addCleanup(preview.finalize)
 
2431
        preview.unversion_file(preview.trans_id_file_id('b-id'))
 
2432
        c_trans_id = preview.trans_id_file_id('c-id')
 
2433
        preview.unversion_file(c_trans_id)
 
2434
        preview.version_file('c-id', c_trans_id)
 
2435
        preview_tree = preview.get_preview_tree()
 
2436
        self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
 
2437
                         preview_tree.all_file_ids())
 
2438
 
 
2439
    def test_path2id_deleted_unchanged(self):
 
2440
        tree = self.make_branch_and_tree('tree')
 
2441
        self.build_tree(['tree/unchanged', 'tree/deleted'])
 
2442
        tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
 
2443
        preview = TransformPreview(tree)
 
2444
        self.addCleanup(preview.finalize)
 
2445
        preview.unversion_file(preview.trans_id_file_id('deleted-id'))
 
2446
        preview_tree = preview.get_preview_tree()
 
2447
        self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
 
2448
        self.assertIs(None, preview_tree.path2id('deleted'))
 
2449
 
 
2450
    def test_path2id_created(self):
 
2451
        tree = self.make_branch_and_tree('tree')
 
2452
        self.build_tree(['tree/unchanged'])
 
2453
        tree.add(['unchanged'], ['unchanged-id'])
 
2454
        preview = TransformPreview(tree)
 
2455
        self.addCleanup(preview.finalize)
 
2456
        preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
 
2457
            'contents', 'new-id')
 
2458
        preview_tree = preview.get_preview_tree()
 
2459
        self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
 
2460
 
 
2461
    def test_path2id_moved(self):
 
2462
        tree = self.make_branch_and_tree('tree')
 
2463
        self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
 
2464
        tree.add(['old_parent', 'old_parent/child'],
 
2465
                 ['old_parent-id', 'child-id'])
 
2466
        preview = TransformPreview(tree)
 
2467
        self.addCleanup(preview.finalize)
 
2468
        new_parent = preview.new_directory('new_parent', preview.root,
 
2469
                                           'new_parent-id')
 
2470
        preview.adjust_path('child', new_parent,
 
2471
                            preview.trans_id_file_id('child-id'))
 
2472
        preview_tree = preview.get_preview_tree()
 
2473
        self.assertIs(None, preview_tree.path2id('old_parent/child'))
 
2474
        self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
 
2475
 
 
2476
    def test_path2id_renamed_parent(self):
 
2477
        tree = self.make_branch_and_tree('tree')
 
2478
        self.build_tree(['tree/old_name/', 'tree/old_name/child'])
 
2479
        tree.add(['old_name', 'old_name/child'],
 
2480
                 ['parent-id', 'child-id'])
 
2481
        preview = TransformPreview(tree)
 
2482
        self.addCleanup(preview.finalize)
 
2483
        preview.adjust_path('new_name', preview.root,
 
2484
                            preview.trans_id_file_id('parent-id'))
 
2485
        preview_tree = preview.get_preview_tree()
 
2486
        self.assertIs(None, preview_tree.path2id('old_name/child'))
 
2487
        self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
 
2488
 
 
2489
    def assertMatchingIterEntries(self, tt, specific_file_ids=None):
 
2490
        preview_tree = tt.get_preview_tree()
 
2491
        preview_result = list(preview_tree.iter_entries_by_dir(
 
2492
                              specific_file_ids))
 
2493
        tree = tt._tree
 
2494
        tt.apply()
 
2495
        actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
 
2496
        self.assertEqual(actual_result, preview_result)
 
2497
 
 
2498
    def test_iter_entries_by_dir_new(self):
 
2499
        tree = self.make_branch_and_tree('tree')
 
2500
        tt = TreeTransform(tree)
 
2501
        tt.new_file('new', tt.root, 'contents', 'new-id')
 
2502
        self.assertMatchingIterEntries(tt)
 
2503
 
 
2504
    def test_iter_entries_by_dir_deleted(self):
 
2505
        tree = self.make_branch_and_tree('tree')
 
2506
        self.build_tree(['tree/deleted'])
 
2507
        tree.add('deleted', 'deleted-id')
 
2508
        tt = TreeTransform(tree)
 
2509
        tt.delete_contents(tt.trans_id_file_id('deleted-id'))
 
2510
        self.assertMatchingIterEntries(tt)
 
2511
 
 
2512
    def test_iter_entries_by_dir_unversioned(self):
 
2513
        tree = self.make_branch_and_tree('tree')
 
2514
        self.build_tree(['tree/removed'])
 
2515
        tree.add('removed', 'removed-id')
 
2516
        tt = TreeTransform(tree)
 
2517
        tt.unversion_file(tt.trans_id_file_id('removed-id'))
 
2518
        self.assertMatchingIterEntries(tt)
 
2519
 
 
2520
    def test_iter_entries_by_dir_moved(self):
 
2521
        tree = self.make_branch_and_tree('tree')
 
2522
        self.build_tree(['tree/moved', 'tree/new_parent/'])
 
2523
        tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
 
2524
        tt = TreeTransform(tree)
 
2525
        tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
 
2526
                       tt.trans_id_file_id('moved-id'))
 
2527
        self.assertMatchingIterEntries(tt)
 
2528
 
 
2529
    def test_iter_entries_by_dir_specific_file_ids(self):
 
2530
        tree = self.make_branch_and_tree('tree')
 
2531
        tree.set_root_id('tree-root-id')
 
2532
        self.build_tree(['tree/parent/', 'tree/parent/child'])
 
2533
        tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
 
2534
        tt = TreeTransform(tree)
 
2535
        self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
 
2536
 
 
2537
    def test_symlink_content_summary(self):
 
2538
        self.requireFeature(SymlinkFeature)
 
2539
        preview = self.get_empty_preview()
 
2540
        preview.new_symlink('path', preview.root, 'target', 'path-id')
 
2541
        summary = preview.get_preview_tree().path_content_summary('path')
 
2542
        self.assertEqual(('symlink', None, None, 'target'), summary)
 
2543
 
 
2544
    def test_missing_content_summary(self):
 
2545
        preview = self.get_empty_preview()
 
2546
        summary = preview.get_preview_tree().path_content_summary('path')
 
2547
        self.assertEqual(('missing', None, None, None), summary)
 
2548
 
 
2549
    def test_deleted_content_summary(self):
 
2550
        tree = self.make_branch_and_tree('tree')
 
2551
        self.build_tree(['tree/path/'])
 
2552
        tree.add('path')
 
2553
        preview = TransformPreview(tree)
 
2554
        self.addCleanup(preview.finalize)
 
2555
        preview.delete_contents(preview.trans_id_tree_path('path'))
 
2556
        summary = preview.get_preview_tree().path_content_summary('path')
 
2557
        self.assertEqual(('missing', None, None, None), summary)
 
2558
 
 
2559
    def test_file_content_summary_executable(self):
 
2560
        preview = self.get_empty_preview()
 
2561
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
 
2562
        preview.set_executability(True, path_id)
 
2563
        summary = preview.get_preview_tree().path_content_summary('path')
 
2564
        self.assertEqual(4, len(summary))
 
2565
        self.assertEqual('file', summary[0])
 
2566
        # size must be known
 
2567
        self.assertEqual(len('contents'), summary[1])
 
2568
        # executable
 
2569
        self.assertEqual(True, summary[2])
 
2570
        # will not have hash (not cheap to determine)
 
2571
        self.assertIs(None, summary[3])
 
2572
 
 
2573
    def test_change_executability(self):
 
2574
        tree = self.make_branch_and_tree('tree')
 
2575
        self.build_tree(['tree/path'])
 
2576
        tree.add('path')
 
2577
        preview = TransformPreview(tree)
 
2578
        self.addCleanup(preview.finalize)
 
2579
        path_id = preview.trans_id_tree_path('path')
 
2580
        preview.set_executability(True, path_id)
 
2581
        summary = preview.get_preview_tree().path_content_summary('path')
 
2582
        self.assertEqual(True, summary[2])
 
2583
 
 
2584
    def test_file_content_summary_non_exec(self):
 
2585
        preview = self.get_empty_preview()
 
2586
        preview.new_file('path', preview.root, 'contents', 'path-id')
 
2587
        summary = preview.get_preview_tree().path_content_summary('path')
 
2588
        self.assertEqual(4, len(summary))
 
2589
        self.assertEqual('file', summary[0])
 
2590
        # size must be known
 
2591
        self.assertEqual(len('contents'), summary[1])
 
2592
        # not executable
 
2593
        self.assertEqual(False, summary[2])
 
2594
        # will not have hash (not cheap to determine)
 
2595
        self.assertIs(None, summary[3])
 
2596
 
 
2597
    def test_dir_content_summary(self):
 
2598
        preview = self.get_empty_preview()
 
2599
        preview.new_directory('path', preview.root, 'path-id')
 
2600
        summary = preview.get_preview_tree().path_content_summary('path')
 
2601
        self.assertEqual(('directory', None, None, None), summary)
 
2602
 
 
2603
    def test_tree_content_summary(self):
 
2604
        preview = self.get_empty_preview()
 
2605
        path = preview.new_directory('path', preview.root, 'path-id')
 
2606
        preview.set_tree_reference('rev-1', path)
 
2607
        summary = preview.get_preview_tree().path_content_summary('path')
 
2608
        self.assertEqual(4, len(summary))
 
2609
        self.assertEqual('tree-reference', summary[0])
 
2610
 
 
2611
    def test_annotate(self):
 
2612
        tree = self.make_branch_and_tree('tree')
 
2613
        self.build_tree_contents([('tree/file', 'a\n')])
 
2614
        tree.add('file', 'file-id')
 
2615
        tree.commit('a', rev_id='one')
 
2616
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
2617
        preview = TransformPreview(tree)
 
2618
        self.addCleanup(preview.finalize)
 
2619
        file_trans_id = preview.trans_id_file_id('file-id')
 
2620
        preview.delete_contents(file_trans_id)
 
2621
        preview.create_file('a\nb\nc\n', file_trans_id)
 
2622
        preview_tree = preview.get_preview_tree()
 
2623
        expected = [
 
2624
            ('one', 'a\n'),
 
2625
            ('me:', 'b\n'),
 
2626
            ('me:', 'c\n'),
 
2627
        ]
 
2628
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2629
        self.assertEqual(expected, annotation)
 
2630
 
 
2631
    def test_annotate_missing(self):
 
2632
        preview = self.get_empty_preview()
 
2633
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2634
        preview_tree = preview.get_preview_tree()
 
2635
        expected = [
 
2636
            ('me:', 'a\n'),
 
2637
            ('me:', 'b\n'),
 
2638
            ('me:', 'c\n'),
 
2639
         ]
 
2640
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2641
        self.assertEqual(expected, annotation)
 
2642
 
 
2643
    def test_annotate_rename(self):
 
2644
        tree = self.make_branch_and_tree('tree')
 
2645
        self.build_tree_contents([('tree/file', 'a\n')])
 
2646
        tree.add('file', 'file-id')
 
2647
        tree.commit('a', rev_id='one')
 
2648
        preview = TransformPreview(tree)
 
2649
        self.addCleanup(preview.finalize)
 
2650
        file_trans_id = preview.trans_id_file_id('file-id')
 
2651
        preview.adjust_path('newname', preview.root, file_trans_id)
 
2652
        preview_tree = preview.get_preview_tree()
 
2653
        expected = [
 
2654
            ('one', 'a\n'),
 
2655
        ]
 
2656
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2657
        self.assertEqual(expected, annotation)
 
2658
 
 
2659
    def test_annotate_deleted(self):
 
2660
        tree = self.make_branch_and_tree('tree')
 
2661
        self.build_tree_contents([('tree/file', 'a\n')])
 
2662
        tree.add('file', 'file-id')
 
2663
        tree.commit('a', rev_id='one')
 
2664
        self.build_tree_contents([('tree/file', 'a\nb\n')])
 
2665
        preview = TransformPreview(tree)
 
2666
        self.addCleanup(preview.finalize)
 
2667
        file_trans_id = preview.trans_id_file_id('file-id')
 
2668
        preview.delete_contents(file_trans_id)
 
2669
        preview_tree = preview.get_preview_tree()
 
2670
        annotation = preview_tree.annotate_iter('file-id', 'me:')
 
2671
        self.assertIs(None, annotation)
 
2672
 
 
2673
    def test_stored_kind(self):
 
2674
        preview = self.get_empty_preview()
 
2675
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2676
        preview_tree = preview.get_preview_tree()
 
2677
        self.assertEqual('file', preview_tree.stored_kind('file-id'))
 
2678
 
 
2679
    def test_is_executable(self):
 
2680
        preview = self.get_empty_preview()
 
2681
        preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
 
2682
        preview.set_executability(True, preview.trans_id_file_id('file-id'))
 
2683
        preview_tree = preview.get_preview_tree()
 
2684
        self.assertEqual(True, preview_tree.is_executable('file-id'))
 
2685
 
 
2686
    def test_get_set_parent_ids(self):
 
2687
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
 
2688
        self.assertEqual([], preview_tree.get_parent_ids())
 
2689
        preview_tree.set_parent_ids(['rev-1'])
 
2690
        self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
 
2691
 
 
2692
    def test_plan_file_merge(self):
 
2693
        work_a = self.make_branch_and_tree('wta')
 
2694
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
 
2695
        work_a.add('file', 'file-id')
 
2696
        base_id = work_a.commit('base version')
 
2697
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
2698
        preview = TransformPreview(work_a)
 
2699
        self.addCleanup(preview.finalize)
 
2700
        trans_id = preview.trans_id_file_id('file-id')
 
2701
        preview.delete_contents(trans_id)
 
2702
        preview.create_file('b\nc\nd\ne\n', trans_id)
 
2703
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
2704
        tree_a = preview.get_preview_tree()
 
2705
        tree_a.set_parent_ids([base_id])
 
2706
        self.assertEqual([
 
2707
            ('killed-a', 'a\n'),
 
2708
            ('killed-b', 'b\n'),
 
2709
            ('unchanged', 'c\n'),
 
2710
            ('unchanged', 'd\n'),
 
2711
            ('new-a', 'e\n'),
 
2712
            ('new-b', 'f\n'),
 
2713
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
2714
 
 
2715
    def test_plan_file_merge_revision_tree(self):
 
2716
        work_a = self.make_branch_and_tree('wta')
 
2717
        self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
 
2718
        work_a.add('file', 'file-id')
 
2719
        base_id = work_a.commit('base version')
 
2720
        tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
 
2721
        preview = TransformPreview(work_a.basis_tree())
 
2722
        self.addCleanup(preview.finalize)
 
2723
        trans_id = preview.trans_id_file_id('file-id')
 
2724
        preview.delete_contents(trans_id)
 
2725
        preview.create_file('b\nc\nd\ne\n', trans_id)
 
2726
        self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
 
2727
        tree_a = preview.get_preview_tree()
 
2728
        tree_a.set_parent_ids([base_id])
 
2729
        self.assertEqual([
 
2730
            ('killed-a', 'a\n'),
 
2731
            ('killed-b', 'b\n'),
 
2732
            ('unchanged', 'c\n'),
 
2733
            ('unchanged', 'd\n'),
 
2734
            ('new-a', 'e\n'),
 
2735
            ('new-b', 'f\n'),
 
2736
        ], list(tree_a.plan_file_merge('file-id', tree_b)))
 
2737
 
 
2738
    def test_walkdirs(self):
 
2739
        preview = self.get_empty_preview()
 
2740
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2741
        # FIXME: new_directory should mark root.
 
2742
        preview.fixup_new_roots()
 
2743
        preview_tree = preview.get_preview_tree()
 
2744
        file_trans_id = preview.new_file('a', preview.root, 'contents',
 
2745
                                         'a-id')
 
2746
        expected = [(('', 'tree-root'),
 
2747
                    [('a', 'a', 'file', None, 'a-id', 'file')])]
 
2748
        self.assertEqual(expected, list(preview_tree.walkdirs()))
 
2749
 
 
2750
    def test_extras(self):
 
2751
        work_tree = self.make_branch_and_tree('tree')
 
2752
        self.build_tree(['tree/removed-file', 'tree/existing-file',
 
2753
                         'tree/not-removed-file'])
 
2754
        work_tree.add(['removed-file', 'not-removed-file'])
 
2755
        preview = TransformPreview(work_tree)
 
2756
        self.addCleanup(preview.finalize)
 
2757
        preview.new_file('new-file', preview.root, 'contents')
 
2758
        preview.new_file('new-versioned-file', preview.root, 'contents',
 
2759
                         'new-versioned-id')
 
2760
        tree = preview.get_preview_tree()
 
2761
        preview.unversion_file(preview.trans_id_tree_path('removed-file'))
 
2762
        self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
 
2763
                         set(tree.extras()))
 
2764
 
 
2765
    def test_merge_into_preview(self):
 
2766
        work_tree = self.make_branch_and_tree('tree')
 
2767
        self.build_tree_contents([('tree/file','b\n')])
 
2768
        work_tree.add('file', 'file-id')
 
2769
        work_tree.commit('first commit')
 
2770
        child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
 
2771
        self.build_tree_contents([('child/file','b\nc\n')])
 
2772
        child_tree.commit('child commit')
 
2773
        child_tree.lock_write()
 
2774
        self.addCleanup(child_tree.unlock)
 
2775
        work_tree.lock_write()
 
2776
        self.addCleanup(work_tree.unlock)
 
2777
        preview = TransformPreview(work_tree)
 
2778
        self.addCleanup(preview.finalize)
 
2779
        file_trans_id = preview.trans_id_file_id('file-id')
 
2780
        preview.delete_contents(file_trans_id)
 
2781
        preview.create_file('a\nb\n', file_trans_id)
 
2782
        preview_tree = preview.get_preview_tree()
 
2783
        merger = Merger.from_revision_ids(None, preview_tree,
 
2784
                                          child_tree.branch.last_revision(),
 
2785
                                          other_branch=child_tree.branch,
 
2786
                                          tree_branch=work_tree.branch)
 
2787
        merger.merge_type = Merge3Merger
 
2788
        tt = merger.make_merger().make_preview_transform()
 
2789
        self.addCleanup(tt.finalize)
 
2790
        final_tree = tt.get_preview_tree()
 
2791
        self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
 
2792
 
 
2793
    def test_merge_preview_into_workingtree(self):
 
2794
        tree = self.make_branch_and_tree('tree')
 
2795
        tree.set_root_id('TREE_ROOT')
 
2796
        tt = TransformPreview(tree)
 
2797
        self.addCleanup(tt.finalize)
 
2798
        tt.new_file('name', tt.root, 'content', 'file-id')
 
2799
        tree2 = self.make_branch_and_tree('tree2')
 
2800
        tree2.set_root_id('TREE_ROOT')
 
2801
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2802
                                         None, tree.basis_tree())
 
2803
        merger.merge_type = Merge3Merger
 
2804
        merger.do_merge()
 
2805
 
 
2806
    def test_merge_preview_into_workingtree_handles_conflicts(self):
 
2807
        tree = self.make_branch_and_tree('tree')
 
2808
        self.build_tree_contents([('tree/foo', 'bar')])
 
2809
        tree.add('foo', 'foo-id')
 
2810
        tree.commit('foo')
 
2811
        tt = TransformPreview(tree)
 
2812
        self.addCleanup(tt.finalize)
 
2813
        trans_id = tt.trans_id_file_id('foo-id')
 
2814
        tt.delete_contents(trans_id)
 
2815
        tt.create_file('baz', trans_id)
 
2816
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
 
2817
        self.build_tree_contents([('tree2/foo', 'qux')])
 
2818
        pb = None
 
2819
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
 
2820
                                         pb, tree.basis_tree())
 
2821
        merger.merge_type = Merge3Merger
 
2822
        merger.do_merge()
 
2823
 
 
2824
    def test_is_executable(self):
 
2825
        tree = self.make_branch_and_tree('tree')
 
2826
        preview = TransformPreview(tree)
 
2827
        self.addCleanup(preview.finalize)
 
2828
        preview.new_file('foo', preview.root, 'bar', 'baz-id')
 
2829
        preview_tree = preview.get_preview_tree()
 
2830
        self.assertEqual(False, preview_tree.is_executable('baz-id',
 
2831
                                                           'tree/foo'))
 
2832
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
 
2833
 
 
2834
    def test_commit_preview_tree(self):
 
2835
        tree = self.make_branch_and_tree('tree')
 
2836
        rev_id = tree.commit('rev1')
 
2837
        tree.branch.lock_write()
 
2838
        self.addCleanup(tree.branch.unlock)
 
2839
        tt = TransformPreview(tree)
 
2840
        tt.new_file('file', tt.root, 'contents', 'file_id')
 
2841
        self.addCleanup(tt.finalize)
 
2842
        preview = tt.get_preview_tree()
 
2843
        preview.set_parent_ids([rev_id])
 
2844
        builder = tree.branch.get_commit_builder([rev_id])
 
2845
        list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
 
2846
        builder.finish_inventory()
 
2847
        rev2_id = builder.commit('rev2')
 
2848
        rev2_tree = tree.branch.repository.revision_tree(rev2_id)
 
2849
        self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
 
2850
 
 
2851
    def test_ascii_limbo_paths(self):
 
2852
        self.requireFeature(tests.UnicodeFilenameFeature)
 
2853
        branch = self.make_branch('any')
 
2854
        tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
 
2855
        tt = TransformPreview(tree)
 
2856
        self.addCleanup(tt.finalize)
 
2857
        foo_id = tt.new_directory('', ROOT_PARENT)
 
2858
        bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
 
2859
        limbo_path = tt._limbo_name(bar_id)
 
2860
        self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
 
2861
 
 
2862
 
 
2863
class FakeSerializer(object):
 
2864
    """Serializer implementation that simply returns the input.
 
2865
 
 
2866
    The input is returned in the order used by pack.ContainerPushParser.
 
2867
    """
 
2868
    @staticmethod
 
2869
    def bytes_record(bytes, names):
 
2870
        return names, bytes
 
2871
 
 
2872
 
 
2873
class TestSerializeTransform(tests.TestCaseWithTransport):
 
2874
 
 
2875
    _test_needs_features = [tests.UnicodeFilenameFeature]
 
2876
 
 
2877
    def get_preview(self, tree=None):
 
2878
        if tree is None:
 
2879
            tree = self.make_branch_and_tree('tree')
 
2880
        tt = TransformPreview(tree)
 
2881
        self.addCleanup(tt.finalize)
 
2882
        return tt
 
2883
 
 
2884
    def assertSerializesTo(self, expected, tt):
 
2885
        records = list(tt.serialize(FakeSerializer()))
 
2886
        self.assertEqual(expected, records)
 
2887
 
 
2888
    @staticmethod
 
2889
    def default_attribs():
 
2890
        return {
 
2891
            '_id_number': 1,
 
2892
            '_new_name': {},
 
2893
            '_new_parent': {},
 
2894
            '_new_executability': {},
 
2895
            '_new_id': {},
 
2896
            '_tree_path_ids': {'': 'new-0'},
 
2897
            '_removed_id': [],
 
2898
            '_removed_contents': [],
 
2899
            '_non_present_ids': {},
 
2900
            }
 
2901
 
 
2902
    def make_records(self, attribs, contents):
 
2903
        records = [
 
2904
            (((('attribs'),),), bencode.bencode(attribs))]
 
2905
        records.extend([(((n, k),), c) for n, k, c in contents])
 
2906
        return records
 
2907
 
 
2908
    def creation_records(self):
 
2909
        attribs = self.default_attribs()
 
2910
        attribs['_id_number'] = 3
 
2911
        attribs['_new_name'] = {
 
2912
            'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
 
2913
        attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
 
2914
        attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
 
2915
        attribs['_new_executability'] = {'new-1': 1}
 
2916
        contents = [
 
2917
            ('new-1', 'file', 'i 1\nbar\n'),
 
2918
            ('new-2', 'directory', ''),
 
2919
            ]
 
2920
        return self.make_records(attribs, contents)
 
2921
 
 
2922
    def test_serialize_creation(self):
 
2923
        tt = self.get_preview()
 
2924
        tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
 
2925
        tt.new_directory('qux', tt.root, 'quxx')
 
2926
        self.assertSerializesTo(self.creation_records(), tt)
 
2927
 
 
2928
    def test_deserialize_creation(self):
 
2929
        tt = self.get_preview()
 
2930
        tt.deserialize(iter(self.creation_records()))
 
2931
        self.assertEqual(3, tt._id_number)
 
2932
        self.assertEqual({'new-1': u'foo\u1234',
 
2933
                          'new-2': 'qux'}, tt._new_name)
 
2934
        self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
 
2935
        self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
 
2936
        self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
 
2937
        self.assertEqual({'new-1': True}, tt._new_executability)
 
2938
        self.assertEqual({'new-1': 'file',
 
2939
                          'new-2': 'directory'}, tt._new_contents)
 
2940
        foo_limbo = open(tt._limbo_name('new-1'), 'rb')
 
2941
        try:
 
2942
            foo_content = foo_limbo.read()
 
2943
        finally:
 
2944
            foo_limbo.close()
 
2945
        self.assertEqual('bar', foo_content)
 
2946
 
 
2947
    def symlink_creation_records(self):
 
2948
        attribs = self.default_attribs()
 
2949
        attribs['_id_number'] = 2
 
2950
        attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
 
2951
        attribs['_new_parent'] = {'new-1': 'new-0'}
 
2952
        contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
 
2953
        return self.make_records(attribs, contents)
 
2954
 
 
2955
    def test_serialize_symlink_creation(self):
 
2956
        self.requireFeature(tests.SymlinkFeature)
 
2957
        tt = self.get_preview()
 
2958
        tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
 
2959
        self.assertSerializesTo(self.symlink_creation_records(), tt)
 
2960
 
 
2961
    def test_deserialize_symlink_creation(self):
 
2962
        self.requireFeature(tests.SymlinkFeature)
 
2963
        tt = self.get_preview()
 
2964
        tt.deserialize(iter(self.symlink_creation_records()))
 
2965
        abspath = tt._limbo_name('new-1')
 
2966
        foo_content = osutils.readlink(abspath)
 
2967
        self.assertEqual(u'bar\u1234', foo_content)
 
2968
 
 
2969
    def make_destruction_preview(self):
 
2970
        tree = self.make_branch_and_tree('.')
 
2971
        self.build_tree([u'foo\u1234', 'bar'])
 
2972
        tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
 
2973
        return self.get_preview(tree)
 
2974
 
 
2975
    def destruction_records(self):
 
2976
        attribs = self.default_attribs()
 
2977
        attribs['_id_number'] = 3
 
2978
        attribs['_removed_id'] = ['new-1']
 
2979
        attribs['_removed_contents'] = ['new-2']
 
2980
        attribs['_tree_path_ids'] = {
 
2981
            '': 'new-0',
 
2982
            u'foo\u1234'.encode('utf-8'): 'new-1',
 
2983
            'bar': 'new-2',
 
2984
            }
 
2985
        return self.make_records(attribs, [])
 
2986
 
 
2987
    def test_serialize_destruction(self):
 
2988
        tt = self.make_destruction_preview()
 
2989
        foo_trans_id = tt.trans_id_tree_file_id('foo-id')
 
2990
        tt.unversion_file(foo_trans_id)
 
2991
        bar_trans_id = tt.trans_id_tree_file_id('bar-id')
 
2992
        tt.delete_contents(bar_trans_id)
 
2993
        self.assertSerializesTo(self.destruction_records(), tt)
 
2994
 
 
2995
    def test_deserialize_destruction(self):
 
2996
        tt = self.make_destruction_preview()
 
2997
        tt.deserialize(iter(self.destruction_records()))
 
2998
        self.assertEqual({u'foo\u1234': 'new-1',
 
2999
                          'bar': 'new-2',
 
3000
                          '': tt.root}, tt._tree_path_ids)
 
3001
        self.assertEqual({'new-1': u'foo\u1234',
 
3002
                          'new-2': 'bar',
 
3003
                          tt.root: ''}, tt._tree_id_paths)
 
3004
        self.assertEqual(set(['new-1']), tt._removed_id)
 
3005
        self.assertEqual(set(['new-2']), tt._removed_contents)
 
3006
 
 
3007
    def missing_records(self):
 
3008
        attribs = self.default_attribs()
 
3009
        attribs['_id_number'] = 2
 
3010
        attribs['_non_present_ids'] = {
 
3011
            'boo': 'new-1',}
 
3012
        return self.make_records(attribs, [])
 
3013
 
 
3014
    def test_serialize_missing(self):
 
3015
        tt = self.get_preview()
 
3016
        boo_trans_id = tt.trans_id_file_id('boo')
 
3017
        self.assertSerializesTo(self.missing_records(), tt)
 
3018
 
 
3019
    def test_deserialize_missing(self):
 
3020
        tt = self.get_preview()
 
3021
        tt.deserialize(iter(self.missing_records()))
 
3022
        self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
 
3023
 
 
3024
    def make_modification_preview(self):
 
3025
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3026
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3027
        tree = self.make_branch_and_tree('tree')
 
3028
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3029
        tree.add('file', 'file-id')
 
3030
        return self.get_preview(tree), LINES_TWO
 
3031
 
 
3032
    def modification_records(self):
 
3033
        attribs = self.default_attribs()
 
3034
        attribs['_id_number'] = 2
 
3035
        attribs['_tree_path_ids'] = {
 
3036
            'file': 'new-1',
 
3037
            '': 'new-0',}
 
3038
        attribs['_removed_contents'] = ['new-1']
 
3039
        contents = [('new-1', 'file',
 
3040
                     'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
 
3041
        return self.make_records(attribs, contents)
 
3042
 
 
3043
    def test_serialize_modification(self):
 
3044
        tt, LINES = self.make_modification_preview()
 
3045
        trans_id = tt.trans_id_file_id('file-id')
 
3046
        tt.delete_contents(trans_id)
 
3047
        tt.create_file(LINES, trans_id)
 
3048
        self.assertSerializesTo(self.modification_records(), tt)
 
3049
 
 
3050
    def test_deserialize_modification(self):
 
3051
        tt, LINES = self.make_modification_preview()
 
3052
        tt.deserialize(iter(self.modification_records()))
 
3053
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3054
 
 
3055
    def make_kind_change_preview(self):
 
3056
        LINES = 'a\nb\nc\nd\n'
 
3057
        tree = self.make_branch_and_tree('tree')
 
3058
        self.build_tree(['tree/foo/'])
 
3059
        tree.add('foo', 'foo-id')
 
3060
        return self.get_preview(tree), LINES
 
3061
 
 
3062
    def kind_change_records(self):
 
3063
        attribs = self.default_attribs()
 
3064
        attribs['_id_number'] = 2
 
3065
        attribs['_tree_path_ids'] = {
 
3066
            'foo': 'new-1',
 
3067
            '': 'new-0',}
 
3068
        attribs['_removed_contents'] = ['new-1']
 
3069
        contents = [('new-1', 'file',
 
3070
                     'i 4\na\nb\nc\nd\n\n')]
 
3071
        return self.make_records(attribs, contents)
 
3072
 
 
3073
    def test_serialize_kind_change(self):
 
3074
        tt, LINES = self.make_kind_change_preview()
 
3075
        trans_id = tt.trans_id_file_id('foo-id')
 
3076
        tt.delete_contents(trans_id)
 
3077
        tt.create_file(LINES, trans_id)
 
3078
        self.assertSerializesTo(self.kind_change_records(), tt)
 
3079
 
 
3080
    def test_deserialize_kind_change(self):
 
3081
        tt, LINES = self.make_kind_change_preview()
 
3082
        tt.deserialize(iter(self.kind_change_records()))
 
3083
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3084
 
 
3085
    def make_add_contents_preview(self):
 
3086
        LINES = 'a\nb\nc\nd\n'
 
3087
        tree = self.make_branch_and_tree('tree')
 
3088
        self.build_tree(['tree/foo'])
 
3089
        tree.add('foo')
 
3090
        os.unlink('tree/foo')
 
3091
        return self.get_preview(tree), LINES
 
3092
 
 
3093
    def add_contents_records(self):
 
3094
        attribs = self.default_attribs()
 
3095
        attribs['_id_number'] = 2
 
3096
        attribs['_tree_path_ids'] = {
 
3097
            'foo': 'new-1',
 
3098
            '': 'new-0',}
 
3099
        contents = [('new-1', 'file',
 
3100
                     'i 4\na\nb\nc\nd\n\n')]
 
3101
        return self.make_records(attribs, contents)
 
3102
 
 
3103
    def test_serialize_add_contents(self):
 
3104
        tt, LINES = self.make_add_contents_preview()
 
3105
        trans_id = tt.trans_id_tree_path('foo')
 
3106
        tt.create_file(LINES, trans_id)
 
3107
        self.assertSerializesTo(self.add_contents_records(), tt)
 
3108
 
 
3109
    def test_deserialize_add_contents(self):
 
3110
        tt, LINES = self.make_add_contents_preview()
 
3111
        tt.deserialize(iter(self.add_contents_records()))
 
3112
        self.assertFileEqual(LINES, tt._limbo_name('new-1'))
 
3113
 
 
3114
    def test_get_parents_lines(self):
 
3115
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3116
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3117
        tree = self.make_branch_and_tree('tree')
 
3118
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3119
        tree.add('file', 'file-id')
 
3120
        tt = self.get_preview(tree)
 
3121
        trans_id = tt.trans_id_tree_path('file')
 
3122
        self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
 
3123
            tt._get_parents_lines(trans_id))
 
3124
 
 
3125
    def test_get_parents_texts(self):
 
3126
        LINES_ONE = 'aa\nbb\ncc\ndd\n'
 
3127
        LINES_TWO = 'z\nbb\nx\ndd\n'
 
3128
        tree = self.make_branch_and_tree('tree')
 
3129
        self.build_tree_contents([('tree/file', LINES_ONE)])
 
3130
        tree.add('file', 'file-id')
 
3131
        tt = self.get_preview(tree)
 
3132
        trans_id = tt.trans_id_tree_path('file')
 
3133
        self.assertEqual((LINES_ONE,),
 
3134
            tt._get_parents_texts(trans_id))