~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_transform.py

  • Committer: Vincent Ladeuil
  • Date: 2010-02-10 15:46:03 UTC
  • mfrom: (4985.3.21 update)
  • mto: This revision was merged to the branch mainline in revision 5021.
  • Revision ID: v.ladeuil+lp@free.fr-20100210154603-k4no1gvfuqpzrw7p
Update performs two merges in a more logical order but stop on conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 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
19
18
from StringIO import StringIO
20
19
import sys
 
20
import time
21
21
 
22
22
from bzrlib import (
 
23
    bencode,
23
24
    errors,
 
25
    filters,
24
26
    generate_ids,
25
27
    osutils,
26
28
    progress,
27
29
    revision as _mod_revision,
28
 
    symbol_versioning,
 
30
    rules,
29
31
    tests,
30
32
    urlutils,
31
33
    )
35
37
                              NonDirectoryParent)
36
38
from bzrlib.diff import show_diff_trees
37
39
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot, 
 
40
                           ReusingTransform, CantMoveRoot,
39
41
                           PathsNotVersionedError, ExistingLimbo,
40
42
                           ExistingPendingDeletion, ImmortalLimbo,
41
43
                           ImmortalPendingDeletion, LockError)
48
50
    TestCaseInTempDir,
49
51
    TestSkipped,
50
52
    )
51
 
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths, 
52
 
                              resolve_conflicts, cook_conflicts, 
 
53
from bzrlib.transform import (TreeTransform, ROOT_PARENT, FinalPaths,
 
54
                              resolve_conflicts, cook_conflicts,
53
55
                              build_tree, get_backup_name,
54
56
                              _FileMover, resolve_checkout,
55
57
                              TransformPreview, create_from_tree)
56
 
from bzrlib.util import bencode
57
58
 
58
59
 
59
60
class TestTreeTransform(tests.TestCaseWithTransport):
129
130
        self.assertIs(self.wt.is_executable('my_pretties2'), False)
130
131
        self.assertEqual('directory', file_kind(self.wt.abspath('oz')))
131
132
        self.assertEqual(len(modified_paths), 3)
132
 
        tree_mod_paths = [self.wt.id2abspath(f) for f in 
 
133
        tree_mod_paths = [self.wt.id2abspath(f) for f in
133
134
                          ('ozzie', 'my_pretties', 'my_pretties2')]
134
135
        self.assertSubset(tree_mod_paths, modified_paths)
135
136
        # is it safe to finalize repeatedly?
136
137
        transform.finalize()
137
138
        transform.finalize()
138
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
 
139
193
    def test_hardlink(self):
140
194
        self.requireFeature(HardlinkFeature)
141
195
        transform, root = self.get_transform()
155
209
        transform, root = self.get_transform()
156
210
        self.wt.lock_tree_write()
157
211
        self.addCleanup(self.wt.unlock)
158
 
        trans_id = transform.new_file('name', root, 'contents', 
 
212
        trans_id = transform.new_file('name', root, 'contents',
159
213
                                      'my_pretties', True)
160
214
        oz = transform.new_directory('oz', root, 'oz-id')
161
215
        dorothy = transform.new_directory('dorothy', oz, 'dorothy-id')
162
 
        toto = transform.new_file('toto', dorothy, 'toto-contents', 
 
216
        toto = transform.new_file('toto', dorothy, 'toto-contents',
163
217
                                  'toto-id', False)
164
218
 
165
219
        self.assertEqual(len(transform.find_conflicts()), 0)
189
243
 
190
244
    def test_conflicts(self):
191
245
        transform, root = self.get_transform()
192
 
        trans_id = transform.new_file('name', root, 'contents', 
 
246
        trans_id = transform.new_file('name', root, 'contents',
193
247
                                      'my_pretties')
194
248
        self.assertEqual(len(transform.find_conflicts()), 0)
195
249
        trans_id2 = transform.new_file('name', root, 'Crontents', 'toto')
196
 
        self.assertEqual(transform.find_conflicts(), 
 
250
        self.assertEqual(transform.find_conflicts(),
197
251
                         [('duplicate', trans_id, trans_id2, 'name')])
198
252
        self.assertRaises(MalformedTransform, transform.apply)
199
253
        transform.adjust_path('name', trans_id, trans_id2)
200
 
        self.assertEqual(transform.find_conflicts(), 
 
254
        self.assertEqual(transform.find_conflicts(),
201
255
                         [('non-directory parent', trans_id)])
202
256
        tinman_id = transform.trans_id_tree_path('tinman')
203
257
        transform.adjust_path('name', tinman_id, trans_id2)
204
 
        self.assertEqual(transform.find_conflicts(), 
205
 
                         [('unversioned parent', tinman_id), 
 
258
        self.assertEqual(transform.find_conflicts(),
 
259
                         [('unversioned parent', tinman_id),
206
260
                          ('missing parent', tinman_id)])
207
261
        lion_id = transform.create_path('lion', root)
208
 
        self.assertEqual(transform.find_conflicts(), 
209
 
                         [('unversioned parent', tinman_id), 
 
262
        self.assertEqual(transform.find_conflicts(),
 
263
                         [('unversioned parent', tinman_id),
210
264
                          ('missing parent', tinman_id)])
211
265
        transform.adjust_path('name', lion_id, trans_id2)
212
 
        self.assertEqual(transform.find_conflicts(), 
 
266
        self.assertEqual(transform.find_conflicts(),
213
267
                         [('unversioned parent', lion_id),
214
268
                          ('missing parent', lion_id)])
215
269
        transform.version_file("Courage", lion_id)
216
 
        self.assertEqual(transform.find_conflicts(), 
217
 
                         [('missing parent', lion_id), 
 
270
        self.assertEqual(transform.find_conflicts(),
 
271
                         [('missing parent', lion_id),
218
272
                          ('versioning no contents', lion_id)])
219
273
        transform.adjust_path('name2', root, trans_id2)
220
 
        self.assertEqual(transform.find_conflicts(), 
 
274
        self.assertEqual(transform.find_conflicts(),
221
275
                         [('versioning no contents', lion_id)])
222
276
        transform.create_file('Contents, okay?', lion_id)
223
277
        transform.adjust_path('name2', trans_id2, trans_id2)
224
 
        self.assertEqual(transform.find_conflicts(), 
225
 
                         [('parent loop', trans_id2), 
 
278
        self.assertEqual(transform.find_conflicts(),
 
279
                         [('parent loop', trans_id2),
226
280
                          ('non-directory parent', trans_id2)])
227
281
        transform.adjust_path('name2', root, trans_id2)
228
282
        oz_id = transform.new_directory('oz', root)
229
283
        transform.set_executability(True, oz_id)
230
 
        self.assertEqual(transform.find_conflicts(), 
 
284
        self.assertEqual(transform.find_conflicts(),
231
285
                         [('unversioned executability', oz_id)])
232
286
        transform.version_file('oz-id', oz_id)
233
 
        self.assertEqual(transform.find_conflicts(), 
 
287
        self.assertEqual(transform.find_conflicts(),
234
288
                         [('non-file executability', oz_id)])
235
289
        transform.set_executability(None, oz_id)
236
290
        tip_id = transform.new_file('tip', oz_id, 'ozma', 'tip-id')
245
299
        self.assert_('oz/tip' in transform2._tree_path_ids)
246
300
        self.assertEqual(fp.get_path(newtip), pathjoin('oz', 'tip'))
247
301
        self.assertEqual(len(result), 2)
248
 
        self.assertEqual((result[0][0], result[0][1]), 
 
302
        self.assertEqual((result[0][0], result[0][1]),
249
303
                         ('duplicate', newtip))
250
 
        self.assertEqual((result[1][0], result[1][2]), 
 
304
        self.assertEqual((result[1][0], result[1][2]),
251
305
                         ('duplicate id', newtip))
252
306
        transform2.finalize()
253
307
        transform3 = TreeTransform(self.wt)
254
308
        self.addCleanup(transform3.finalize)
255
309
        oz_id = transform3.trans_id_tree_file_id('oz-id')
256
310
        transform3.delete_contents(oz_id)
257
 
        self.assertEqual(transform3.find_conflicts(), 
 
311
        self.assertEqual(transform3.find_conflicts(),
258
312
                         [('missing parent', oz_id)])
259
313
        root_id = transform3.root
260
314
        tip_id = transform3.trans_id_tree_file_id('tip-id')
369
423
        self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
370
424
        self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
371
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
 
372
438
    def test_add_del(self):
373
439
        start, root = self.get_transform()
374
440
        start.new_directory('a', root, 'a')
387
453
        self.addCleanup(unversion.finalize)
388
454
        parent = unversion.trans_id_tree_path('parent')
389
455
        unversion.unversion_file(parent)
390
 
        self.assertEqual(unversion.find_conflicts(), 
 
456
        self.assertEqual(unversion.find_conflicts(),
391
457
                         [('unversioned parent', parent_id)])
392
458
        file_id = unversion.trans_id_tree_file_id('child-id')
393
459
        unversion.unversion_file(file_id)
413
479
        mangle_tree.adjust_path('name2', root, name1)
414
480
        mangle_tree.adjust_path('name1', root, name2)
415
481
 
416
 
        #tests for deleting parent directories 
 
482
        #tests for deleting parent directories
417
483
        ddir = mangle_tree.trans_id_tree_file_id('ddir')
418
484
        mangle_tree.delete_contents(ddir)
419
485
        dfile = mangle_tree.trans_id_tree_file_id('dfile')
448
514
        create_tree,root = self.get_transform()
449
515
        newdir = create_tree.new_directory('selftest', root, 'selftest-id')
450
516
        create_tree.new_file('blackbox.py', newdir, 'hello1', 'blackbox-id')
451
 
        create_tree.apply()        
 
517
        create_tree.apply()
452
518
        mangle_tree,root = self.get_transform()
453
519
        selftest = mangle_tree.trans_id_tree_file_id('selftest-id')
454
520
        blackbox = mangle_tree.trans_id_tree_file_id('blackbox-id')
462
528
        bzrlib = create_tree.new_directory('bzrlib', root, 'bzrlib-id')
463
529
        tests = create_tree.new_directory('tests', bzrlib, 'tests-id')
464
530
        blackbox = create_tree.new_directory('blackbox', tests, 'blackbox-id')
465
 
        create_tree.new_file('test_too_much.py', blackbox, 'hello1', 
 
531
        create_tree.new_file('test_too_much.py', blackbox, 'hello1',
466
532
                             'test_too_much-id')
467
 
        create_tree.apply()        
 
533
        create_tree.apply()
468
534
        mangle_tree,root = self.get_transform()
469
535
        bzrlib = mangle_tree.trans_id_tree_file_id('bzrlib-id')
470
536
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
471
537
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
472
538
        mangle_tree.adjust_path('selftest', bzrlib, tests)
473
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
539
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
474
540
        mangle_tree.set_executability(True, test_too_much)
475
541
        mangle_tree.apply()
476
542
 
477
543
    def test_both_rename3(self):
478
544
        create_tree,root = self.get_transform()
479
545
        tests = create_tree.new_directory('tests', root, 'tests-id')
480
 
        create_tree.new_file('test_too_much.py', tests, 'hello1', 
 
546
        create_tree.new_file('test_too_much.py', tests, 'hello1',
481
547
                             'test_too_much-id')
482
 
        create_tree.apply()        
 
548
        create_tree.apply()
483
549
        mangle_tree,root = self.get_transform()
484
550
        tests = mangle_tree.trans_id_tree_file_id('tests-id')
485
551
        test_too_much = mangle_tree.trans_id_tree_file_id('test_too_much-id')
486
552
        mangle_tree.adjust_path('selftest', root, tests)
487
 
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much) 
 
553
        mangle_tree.adjust_path('blackbox.py', tests, test_too_much)
488
554
        mangle_tree.set_executability(True, test_too_much)
489
555
        mangle_tree.apply()
490
556
 
503
569
        newdir = move_id.new_directory('dir', root, 'newdir')
504
570
        move_id.adjust_path('name2', newdir, name1)
505
571
        move_id.apply()
506
 
        
 
572
 
507
573
    def test_replace_dangling_ie(self):
508
574
        create_tree, root = self.get_transform()
509
575
        # prepare tree
525
591
        resolve_conflicts(replace)
526
592
        replace.apply()
527
593
 
528
 
    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
 
529
599
        self.requireFeature(SymlinkFeature)
530
 
        transform,root = self.get_transform()
 
600
        transform, root = self.get_transform()
531
601
        oz_id = transform.new_directory('oz', root, 'oz-id')
532
 
        wizard = transform.new_symlink('wizard', oz_id, 'wizard-target', 
 
602
        wizard = transform.new_symlink(link_name1, oz_id, link_target1,
533
603
                                       'wizard-id')
534
 
        wiz_id = transform.create_path('wizard2', oz_id)
535
 
        transform.create_symlink('behind_curtain', wiz_id)
536
 
        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)
537
607
        transform.set_executability(True, wiz_id)
538
 
        self.assertEqual(transform.find_conflicts(), 
 
608
        self.assertEqual(transform.find_conflicts(),
539
609
                         [('non-file executability', wiz_id)])
540
610
        transform.set_executability(None, wiz_id)
541
611
        transform.apply()
542
 
        self.assertEqual(self.wt.path2id('oz/wizard'), 'wizard-id')
543
 
        self.assertEqual(file_kind(self.wt.abspath('oz/wizard')), 'symlink')
544
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard2')), 
545
 
                         'behind_curtain')
546
 
        self.assertEqual(os.readlink(self.wt.abspath('oz/wizard')),
547
 
                         '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')
548
630
 
549
631
    def test_unable_create_symlink(self):
550
632
        def tt_helper():
574
656
        create.apply()
575
657
        conflicts,root = self.get_transform()
576
658
        # set up duplicate entry, duplicate id
577
 
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy', 
 
659
        new_dorothy = conflicts.new_file('dorothy', root, 'dorothy',
578
660
                                         'dorothy-id')
579
661
        old_dorothy = conflicts.trans_id_tree_file_id('dorothy-id')
580
662
        oz = conflicts.trans_id_tree_file_id('oz-id')
604
686
        tt, emerald, oz, old_dorothy, new_dorothy = self.get_conflicted()
605
687
        raw_conflicts = resolve_conflicts(tt)
606
688
        cooked_conflicts = cook_conflicts(raw_conflicts, tt)
607
 
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved', 
 
689
        duplicate = DuplicateEntry('Moved existing file to', 'dorothy.moved',
608
690
                                   'dorothy', None, 'dorothy-id')
609
691
        self.assertEqual(cooked_conflicts[0], duplicate)
610
 
        duplicate_id = DuplicateID('Unversioned existing file', 
 
692
        duplicate_id = DuplicateID('Unversioned existing file',
611
693
                                   'dorothy.moved', 'dorothy', None,
612
694
                                   'dorothy-id')
613
695
        self.assertEqual(cooked_conflicts[1], duplicate_id)
621
703
        unversioned_parent2 = UnversionedParent('Versioned directory', 'oz',
622
704
                                               'oz-id')
623
705
        self.assertEqual(cooked_conflicts[3], unversioned_parent)
624
 
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity', 
 
706
        parent_loop = ParentLoop('Cancelled move', 'oz/emeraldcity',
625
707
                                 'oz/emeraldcity', 'emerald-id', 'emerald-id')
626
708
        self.assertEqual(cooked_conflicts[4], deleted_parent)
627
709
        self.assertEqual(cooked_conflicts[5], unversioned_parent2)
730
812
        create.apply()
731
813
        transform, root = self.get_transform()
732
814
        transform.adjust_root_path('oldroot', fun)
733
 
        new_root=transform.trans_id_tree_path('')
 
815
        new_root = transform.trans_id_tree_path('')
734
816
        transform.version_file('new-root', new_root)
735
817
        transform.apply()
736
818
 
750
832
 
751
833
    def test_set_executability_order(self):
752
834
        """Ensure that executability behaves the same, no matter what order.
753
 
        
 
835
 
754
836
        - create file and set executability simultaneously
755
837
        - create file and set executability afterward
756
838
        - unsetting the executability of a file whose executability has not been
1410
1492
        # textual merge
1411
1493
        self.assertEqual(this.wt.get_file('a').read(), 'y\nb\nc\nd\bz\n')
1412
1494
        # three-way text conflict
1413
 
        self.assertEqual(this.wt.get_file('b').read(), 
 
1495
        self.assertEqual(this.wt.get_file('b').read(),
1414
1496
                         conflict_text('b', 'b2'))
1415
1497
        # OTHER wins
1416
1498
        self.assertEqual(this.wt.get_file('c').read(), 'c2')
1420
1502
        self.assertEqual(this.wt.get_file('e').read(), 'e2')
1421
1503
        # No change
1422
1504
        self.assertEqual(this.wt.get_file('f').read(), 'f')
1423
 
        # Correct correct results when THIS == OTHER 
 
1505
        # Correct correct results when THIS == OTHER
1424
1506
        self.assertEqual(this.wt.get_file('g').read(), 'g')
1425
1507
        # Text conflict when THIS & OTHER are text and BASE is dir
1426
 
        self.assertEqual(this.wt.get_file('h').read(), 
 
1508
        self.assertEqual(this.wt.get_file('h').read(),
1427
1509
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1428
1510
        self.assertEqual(this.wt.get_file_byname('h.THIS').read(),
1429
1511
                         '1\n2\n3\n4\n')
1430
1512
        self.assertEqual(this.wt.get_file_byname('h.OTHER').read(),
1431
1513
                         'h\ni\nj\nk\n')
1432
1514
        self.assertEqual(file_kind(this.wt.abspath('h.BASE')), 'directory')
1433
 
        self.assertEqual(this.wt.get_file('i').read(), 
 
1515
        self.assertEqual(this.wt.get_file('i').read(),
1434
1516
                         conflict_text('1\n2\n3\n4\n', 'h\ni\nj\nk\n'))
1435
1517
        self.assertEqual(this.wt.get_file_byname('i.THIS').read(),
1436
1518
                         '1\n2\n3\n4\n')
1460
1542
            tg.tt.new_symlink('b', tg.root, 'b', 'b')
1461
1543
            tg.tt.new_file('c', tg.root, 'c', 'c')
1462
1544
            tg.tt.new_symlink('d', tg.root, tg.name, 'd')
1463
 
        targets = ((base, 'base-e', 'base-f', None, None), 
1464
 
                   (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'),
1465
1547
                   (other, 'other-e', None, 'other-g', 'other-h'))
1466
1548
        for tg, e_target, f_target, g_target, h_target in targets:
1467
 
            for link, target in (('e', e_target), ('f', f_target), 
 
1549
            for link, target in (('e', e_target), ('f', f_target),
1468
1550
                                 ('g', g_target), ('h', h_target)):
1469
1551
                if target is not None:
1470
1552
                    tg.tt.new_symlink(link, tg.root, target, link)
1496
1578
        base = TransformGroup("BASE", root_id)
1497
1579
        this = TransformGroup("THIS", root_id)
1498
1580
        other = TransformGroup("OTHER", root_id)
1499
 
        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')
1500
1582
                                   for t in [base, this, other]]
1501
 
        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')
1502
1584
                                   for t in [base, this, other]]
1503
1585
        base.tt.new_directory('c', base_a, 'c')
1504
1586
        this.tt.new_directory('c1', this_a, 'c')
1529
1611
        base = TransformGroup("BASE", root_id)
1530
1612
        this = TransformGroup("THIS", root_id)
1531
1613
        other = TransformGroup("OTHER", root_id)
1532
 
        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')
1533
1615
                                   for t in [base, this, other]]
1534
 
        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')
1535
1617
                                   for t in [base, this, other]]
1536
1618
 
1537
1619
        base.tt.new_file('g', base_a, 'g', 'g')
1628
1710
        os.symlink('foo', 'target2/symlink')
1629
1711
        build_tree(source.basis_tree(), target)
1630
1712
        self.assertEqual([], target.conflicts())
1631
 
        
 
1713
 
1632
1714
    def test_directory_conflict_handling(self):
1633
1715
        """Ensure that when building trees, conflict handling is done"""
1634
1716
        source = self.make_branch_and_tree('source')
1690
1772
        target = self.make_branch_and_tree('target')
1691
1773
        self.build_tree(['target/name'])
1692
1774
        target.add('name')
1693
 
        self.assertRaises(errors.WorkingTreeAlreadyPopulated, 
 
1775
        self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1694
1776
            build_tree, source.basis_tree(), target)
1695
1777
 
1696
1778
    def test_build_tree_rename_count(self):
1840
1922
        self.assertEqual([], list(target.iter_changes(revision_tree)))
1841
1923
        self.assertTrue(source.is_executable('file1-id'))
1842
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
 
1843
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')
1844
1976
        source = self.make_branch_and_tree('source')
1845
1977
        self.build_tree(['source/file', 'source/FILE'])
1846
1978
        source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1854
1986
        self.assertEqual('FILE', target.id2path('upper-id'))
1855
1987
 
1856
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')
 
2093
 
 
2094
 
1857
2095
class MockTransform(object):
1858
2096
 
1859
2097
    def has_named_child(self, by_parent, parent_id, name):
2026
2264
    def create_tree(self):
2027
2265
        tree = self.make_branch_and_tree('.')
2028
2266
        self.build_tree_contents([('a', 'content 1')])
 
2267
        tree.set_root_id('TREE_ROOT')
2029
2268
        tree.add('a', 'a-id')
2030
2269
        tree.commit('rev1', rev_id='rev1')
2031
2270
        return tree.branch.repository.revision_tree('rev1')
2132
2371
    def test_ignore_pb(self):
2133
2372
        # pb could be supported, but TT.iter_changes doesn't support it.
2134
2373
        revision_tree, preview_tree = self.get_tree_and_preview_tree()
2135
 
        preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
 
2374
        preview_tree.iter_changes(revision_tree)
2136
2375
 
2137
2376
    def test_kind(self):
2138
2377
        revision_tree = self.create_tree()
2153
2392
        self.assertEqual(os.stat(limbo_path).st_mtime,
2154
2393
                         preview_tree.get_file_mtime('file-id'))
2155
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
 
2156
2407
    def test_get_file(self):
2157
2408
        preview = self.get_empty_preview()
2158
2409
        preview.new_file('file', preview.root, 'contents', 'file-id')
2306
2557
        self.assertEqual(('missing', None, None, None), summary)
2307
2558
 
2308
2559
    def test_file_content_summary_executable(self):
2309
 
        if not osutils.supports_executable():
2310
 
            raise TestNotApplicable()
2311
2560
        preview = self.get_empty_preview()
2312
2561
        path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2313
2562
        preview.set_executability(True, path_id)
2322
2571
        self.assertIs(None, summary[3])
2323
2572
 
2324
2573
    def test_change_executability(self):
2325
 
        if not osutils.supports_executable():
2326
 
            raise TestNotApplicable()
2327
2574
        tree = self.make_branch_and_tree('tree')
2328
2575
        self.build_tree(['tree/path'])
2329
2576
        tree.add('path')
2343
2590
        # size must be known
2344
2591
        self.assertEqual(len('contents'), summary[1])
2345
2592
        # not executable
2346
 
        if osutils.supports_executable():
2347
 
            self.assertEqual(False, summary[2])
2348
 
        else:
2349
 
            self.assertEqual(None, summary[2])
 
2593
        self.assertEqual(False, summary[2])
2350
2594
        # will not have hash (not cheap to determine)
2351
2595
        self.assertIs(None, summary[3])
2352
2596
 
2493
2737
 
2494
2738
    def test_walkdirs(self):
2495
2739
        preview = self.get_empty_preview()
2496
 
        preview.version_file('tree-root', preview.root)
 
2740
        root = preview.new_directory('', ROOT_PARENT, 'tree-root')
 
2741
        # FIXME: new_directory should mark root.
 
2742
        preview.fixup_new_roots()
2497
2743
        preview_tree = preview.get_preview_tree()
2498
2744
        file_trans_id = preview.new_file('a', preview.root, 'contents',
2499
2745
                                         'a-id')
2530
2776
        self.addCleanup(work_tree.unlock)
2531
2777
        preview = TransformPreview(work_tree)
2532
2778
        self.addCleanup(preview.finalize)
2533
 
        preview_tree = preview.get_preview_tree()
2534
2779
        file_trans_id = preview.trans_id_file_id('file-id')
2535
2780
        preview.delete_contents(file_trans_id)
2536
2781
        preview.create_file('a\nb\n', file_trans_id)
2537
 
        pb = progress.DummyProgress()
2538
 
        merger = Merger.from_revision_ids(pb, preview_tree,
 
2782
        preview_tree = preview.get_preview_tree()
 
2783
        merger = Merger.from_revision_ids(None, preview_tree,
2539
2784
                                          child_tree.branch.last_revision(),
2540
2785
                                          other_branch=child_tree.branch,
2541
2786
                                          tree_branch=work_tree.branch)
2547
2792
 
2548
2793
    def test_merge_preview_into_workingtree(self):
2549
2794
        tree = self.make_branch_and_tree('tree')
 
2795
        tree.set_root_id('TREE_ROOT')
2550
2796
        tt = TransformPreview(tree)
2551
2797
        self.addCleanup(tt.finalize)
2552
2798
        tt.new_file('name', tt.root, 'content', 'file-id')
2553
2799
        tree2 = self.make_branch_and_tree('tree2')
2554
 
        pb = progress.DummyProgress()
 
2800
        tree2.set_root_id('TREE_ROOT')
2555
2801
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2556
 
                                         pb, tree.basis_tree())
 
2802
                                         None, tree.basis_tree())
2557
2803
        merger.merge_type = Merge3Merger
2558
2804
        merger.do_merge()
2559
2805
 
2569
2815
        tt.create_file('baz', trans_id)
2570
2816
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2571
2817
        self.build_tree_contents([('tree2/foo', 'qux')])
2572
 
        pb = progress.DummyProgress()
 
2818
        pb = None
2573
2819
        merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2574
2820
                                         pb, tree.basis_tree())
2575
2821
        merger.merge_type = Merge3Merger
2585
2831
                                                           'tree/foo'))
2586
2832
        self.assertEqual(False, preview_tree.is_executable('baz-id'))
2587
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
 
2588
2862
 
2589
2863
class FakeSerializer(object):
2590
2864
    """Serializer implementation that simply returns the input.
2685
2959
        self.assertSerializesTo(self.symlink_creation_records(), tt)
2686
2960
 
2687
2961
    def test_deserialize_symlink_creation(self):
 
2962
        self.requireFeature(tests.SymlinkFeature)
2688
2963
        tt = self.get_preview()
2689
2964
        tt.deserialize(iter(self.symlink_creation_records()))
2690
 
        # XXX readlink should be returning unicode, not utf-8
2691
 
        foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
 
2965
        abspath = tt._limbo_name('new-1')
 
2966
        foo_content = osutils.readlink(abspath)
2692
2967
        self.assertEqual(u'bar\u1234', foo_content)
2693
2968
 
2694
2969
    def make_destruction_preview(self):