~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

Fix BzrDir.create_workingtree for NULL_REVISION

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006 by Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import os
 
19
import time
19
20
 
 
21
from bzrlib import errors, inventory, osutils
20
22
from bzrlib.branch import Branch
21
 
from bzrlib.clone import copy_branch
22
 
import bzrlib.errors as errors
23
23
from bzrlib.diff import internal_diff
24
 
from bzrlib.inventory import Inventory, ROOT_ID
25
 
import bzrlib.inventory as inventory
26
 
from bzrlib.osutils import has_symlinks, rename, pathjoin
27
 
from bzrlib.tests import TestCase, TestCaseInTempDir
 
24
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
 
25
    InventoryDirectory, InventoryEntry)
 
26
from bzrlib.osutils import (has_symlinks, rename, pathjoin, is_inside_any, 
 
27
    is_inside_or_parent_of_any)
 
28
from bzrlib.tests import TestCase, TestCaseWithTransport
 
29
from bzrlib.transform import TreeTransform
 
30
from bzrlib.uncommit import uncommit
28
31
 
29
32
 
30
33
class TestInventory(TestCase):
31
34
 
32
35
    def test_is_within(self):
33
 
        from bzrlib.osutils import is_inside_any
34
36
 
35
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
36
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
42
44
        for dirs, fn in [(['src'], 'srccontrol'),
43
45
                         (['src'], 'srccontrol/foo')]:
44
46
            self.assertFalse(is_inside_any(dirs, fn))
 
47
 
 
48
    def test_is_within_or_parent(self):
 
49
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
 
50
                         (['src'], 'src/foo.c'),
 
51
                         (['src/bar.c'], 'src'),
 
52
                         (['src/bar.c', 'bla/foo.c'], 'src'),
 
53
                         (['src'], 'src'),
 
54
                         ]:
 
55
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
45
56
            
 
57
        for dirs, fn in [(['src'], 'srccontrol'),
 
58
                         (['srccontrol/foo.c'], 'src'),
 
59
                         (['src'], 'srccontrol/foo')]:
 
60
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
 
61
 
46
62
    def test_ids(self):
47
63
        """Test detection of files within selected directories."""
48
64
        inv = Inventory()
59
75
        
60
76
        self.assert_('src-id' in inv)
61
77
 
62
 
 
 
78
    def test_iter_entries(self):
 
79
        inv = Inventory()
 
80
        
 
81
        for args in [('src', 'directory', 'src-id'), 
 
82
                     ('doc', 'directory', 'doc-id'), 
 
83
                     ('src/hello.c', 'file', 'hello-id'),
 
84
                     ('src/bye.c', 'file', 'bye-id'),
 
85
                     ('Makefile', 'file', 'makefile-id')]:
 
86
            inv.add_path(*args)
 
87
 
 
88
        self.assertEqual([
 
89
            ('', ROOT_ID),
 
90
            ('Makefile', 'makefile-id'),
 
91
            ('doc', 'doc-id'),
 
92
            ('src', 'src-id'),
 
93
            ('src/bye.c', 'bye-id'),
 
94
            ('src/hello.c', 'hello-id'),
 
95
            ], [(path, ie.file_id) for path, ie in inv.iter_entries()])
 
96
            
 
97
    def test_iter_entries_by_dir(self):
 
98
        inv = Inventory()
 
99
        
 
100
        for args in [('src', 'directory', 'src-id'), 
 
101
                     ('doc', 'directory', 'doc-id'), 
 
102
                     ('src/hello.c', 'file', 'hello-id'),
 
103
                     ('src/bye.c', 'file', 'bye-id'),
 
104
                     ('zz', 'file', 'zz-id'),
 
105
                     ('src/sub/', 'directory', 'sub-id'),
 
106
                     ('src/zz.c', 'file', 'zzc-id'),
 
107
                     ('src/sub/a', 'file', 'a-id'),
 
108
                     ('Makefile', 'file', 'makefile-id')]:
 
109
            inv.add_path(*args)
 
110
 
 
111
        self.assertEqual([
 
112
            ('', ROOT_ID),
 
113
            ('Makefile', 'makefile-id'),
 
114
            ('doc', 'doc-id'),
 
115
            ('src', 'src-id'),
 
116
            ('zz', 'zz-id'),
 
117
            ('src/bye.c', 'bye-id'),
 
118
            ('src/hello.c', 'hello-id'),
 
119
            ('src/sub', 'sub-id'),
 
120
            ('src/zz.c', 'zzc-id'),
 
121
            ('src/sub/a', 'a-id'),
 
122
            ], [(path, ie.file_id) for path, ie in inv.iter_entries_by_dir()])
 
123
            
63
124
    def test_version(self):
64
125
        """Inventory remembers the text's version."""
65
126
        inv = Inventory()
132
193
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
133
194
        self.failIf(link.has_text())
134
195
 
135
 
 
136
 
class TestEntryDiffing(TestCaseInTempDir):
 
196
    def test_make_entry(self):
 
197
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
 
198
            inventory.InventoryFile)
 
199
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
 
200
            inventory.InventoryLink)
 
201
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
 
202
            inventory.InventoryDirectory)
 
203
 
 
204
    def test_make_entry_non_normalized(self):
 
205
        orig_normalized_filename = osutils.normalized_filename
 
206
 
 
207
        try:
 
208
            osutils.normalized_filename = osutils._accessible_normalized_filename
 
209
            entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
 
210
            self.assertEqual(u'\xe5', entry.name)
 
211
            self.assertIsInstance(entry, inventory.InventoryFile)
 
212
 
 
213
            osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
214
            self.assertRaises(errors.InvalidNormalization,
 
215
                    inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
 
216
        finally:
 
217
            osutils.normalized_filename = orig_normalized_filename
 
218
 
 
219
 
 
220
class TestEntryDiffing(TestCaseWithTransport):
137
221
 
138
222
    def setUp(self):
139
223
        super(TestEntryDiffing, self).setUp()
140
 
        self.branch = Branch.initialize(u'.')
141
 
        self.wt = self.branch.working_tree()
 
224
        self.wt = self.make_branch_and_tree('.')
 
225
        self.branch = self.wt.branch
142
226
        print >> open('file', 'wb'), 'foo'
143
 
        self.branch.working_tree().add(['file'], ['fileid'])
 
227
        print >> open('binfile', 'wb'), 'foo'
 
228
        self.wt.add(['file'], ['fileid'])
 
229
        self.wt.add(['binfile'], ['binfileid'])
144
230
        if has_symlinks():
145
231
            os.symlink('target1', 'symlink')
146
 
            self.branch.working_tree().add(['symlink'], ['linkid'])
 
232
            self.wt.add(['symlink'], ['linkid'])
147
233
        self.wt.commit('message_1', rev_id = '1')
148
234
        print >> open('file', 'wb'), 'bar'
 
235
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
149
236
        if has_symlinks():
150
237
            os.unlink('symlink')
151
238
            os.symlink('target2', 'symlink')
152
 
        self.tree_1 = self.branch.revision_tree('1')
153
 
        self.inv_1 = self.branch.get_inventory('1')
 
239
        self.tree_1 = self.branch.repository.revision_tree('1')
 
240
        self.inv_1 = self.branch.repository.get_inventory('1')
154
241
        self.file_1 = self.inv_1['fileid']
155
 
        self.tree_2 = self.branch.working_tree()
 
242
        self.file_1b = self.inv_1['binfileid']
 
243
        self.tree_2 = self.wt
156
244
        self.inv_2 = self.tree_2.read_working_inventory()
157
245
        self.file_2 = self.inv_2['fileid']
 
246
        self.file_2b = self.inv_2['binfileid']
158
247
        if has_symlinks():
159
248
            self.link_1 = self.inv_1['linkid']
160
249
            self.link_2 = self.inv_2['linkid']
165
254
                          "old_label", self.tree_1,
166
255
                          "/dev/null", None, None,
167
256
                          output)
168
 
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
169
 
                                            "+++ /dev/null\t\n"
 
257
        self.assertEqual(output.getvalue(), "--- old_label\n"
 
258
                                            "+++ /dev/null\n"
170
259
                                            "@@ -1,1 +0,0 @@\n"
171
260
                                            "-foo\n"
172
261
                                            "\n")
177
266
                          "new_label", self.tree_1,
178
267
                          "/dev/null", None, None,
179
268
                          output, reverse=True)
180
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
181
 
                                            "+++ new_label\t\n"
 
269
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
270
                                            "+++ new_label\n"
182
271
                                            "@@ -0,0 +1,1 @@\n"
183
272
                                            "+foo\n"
184
273
                                            "\n")
189
278
                          "/dev/null", self.tree_1, 
190
279
                          "new_label", self.file_2, self.tree_2,
191
280
                          output)
192
 
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
193
 
                                            "+++ new_label\t\n"
 
281
        self.assertEqual(output.getvalue(), "--- /dev/null\n"
 
282
                                            "+++ new_label\n"
194
283
                                            "@@ -1,1 +1,1 @@\n"
195
284
                                            "-foo\n"
196
285
                                            "+bar\n"
197
286
                                            "\n")
198
287
        
 
288
    def test_file_diff_binary(self):
 
289
        output = StringIO()
 
290
        self.file_1.diff(internal_diff, 
 
291
                          "/dev/null", self.tree_1, 
 
292
                          "new_label", self.file_2b, self.tree_2,
 
293
                          output)
 
294
        self.assertEqual(output.getvalue(), 
 
295
                         "Binary files /dev/null and new_label differ\n")
199
296
    def test_link_diff_deleted(self):
200
297
        if not has_symlinks():
201
298
            return
230
327
                         "=== target changed 'target1' => 'target2'\n")
231
328
 
232
329
 
233
 
class TestSnapshot(TestCaseInTempDir):
 
330
class TestSnapshot(TestCaseWithTransport):
234
331
 
235
332
    def setUp(self):
236
333
        # for full testing we'll need a branch
240
337
        # to change, and then test merge patterns
241
338
        # with fake parent entries.
242
339
        super(TestSnapshot, self).setUp()
243
 
        self.branch = Branch.initialize(u'.')
 
340
        self.wt = self.make_branch_and_tree('.')
 
341
        self.branch = self.wt.branch
244
342
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
245
 
        self.branch.working_tree().add(['subdir', 'subdir/file'],
 
343
        self.wt.add(['subdir', 'subdir/file'],
246
344
                                       ['dirid', 'fileid'])
247
345
        if has_symlinks():
248
346
            pass
249
 
        self.wt = self.branch.working_tree()
250
347
        self.wt.commit('message_1', rev_id = '1')
251
 
        self.tree_1 = self.branch.revision_tree('1')
252
 
        self.inv_1 = self.branch.get_inventory('1')
 
348
        self.tree_1 = self.branch.repository.revision_tree('1')
 
349
        self.inv_1 = self.branch.repository.get_inventory('1')
253
350
        self.file_1 = self.inv_1['fileid']
254
 
        self.work_tree = self.branch.working_tree()
255
 
        self.file_active = self.work_tree.inventory['fileid']
 
351
        self.file_active = self.wt.inventory['fileid']
 
352
        self.builder = self.branch.get_commit_builder([], timestamp=time.time(), revision_id='2')
256
353
 
257
354
    def test_snapshot_new_revision(self):
258
355
        # This tests that a simple commit with no parents makes a new
259
356
        # revision value in the inventory entry
260
 
        self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree, 
261
 
                                  self.branch.weave_store,
262
 
                                  self.branch.get_transaction())
 
357
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, self.builder)
263
358
        # expected outcome - file_1 has a revision id of '2', and we can get
264
359
        # its text of 'file contents' out of the weave.
265
360
        self.assertEqual(self.file_1.revision, '1')
266
361
        self.assertEqual(self.file_active.revision, '2')
267
362
        # this should be a separate test probably, but lets check it once..
268
 
        lines = self.branch.weave_store.get_lines('fileid','2',
269
 
            self.branch.get_transaction())
 
363
        lines = self.branch.repository.weave_store.get_weave(
 
364
            'fileid', 
 
365
            self.branch.get_transaction()).get_lines('2')
270
366
        self.assertEqual(lines, ['contents of subdir/file\n'])
271
367
 
272
368
    def test_snapshot_unchanged(self):
273
369
        #This tests that a simple commit does not make a new entry for
274
370
        # an unchanged inventory entry
275
371
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
276
 
                                  self.work_tree, self.branch.weave_store,
277
 
                                  self.branch.get_transaction())
 
372
                                  self.wt, self.builder)
278
373
        self.assertEqual(self.file_1.revision, '1')
279
374
        self.assertEqual(self.file_active.revision, '1')
280
 
        self.assertRaises(errors.WeaveError,
281
 
                          self.branch.weave_store.get_lines, 'fileid', '2',
282
 
                          self.branch.get_transaction())
 
375
        vf = self.branch.repository.weave_store.get_weave(
 
376
            'fileid', 
 
377
            self.branch.repository.get_transaction())
 
378
        self.assertRaises(errors.RevisionNotPresent,
 
379
                          vf.get_lines,
 
380
                          '2')
283
381
 
284
382
    def test_snapshot_merge_identical_different_revid(self):
285
383
        # This tests that a commit with two identical parents, one of which has
293
391
        self.assertEqual(self.file_1, other_ie)
294
392
        other_ie.revision = 'other'
295
393
        self.assertNotEqual(self.file_1, other_ie)
296
 
        self.branch.weave_store.add_identical_text('fileid', '1', 'other', ['1'],
297
 
            self.branch.get_transaction())
 
394
        versionfile = self.branch.repository.weave_store.get_weave(
 
395
            'fileid', self.branch.repository.get_transaction())
 
396
        versionfile.clone_text('other', '1', ['1'])
298
397
        self.file_active.snapshot('2', 'subdir/file', 
299
398
                                  {'1':self.file_1, 'other':other_ie},
300
 
                                  self.work_tree, self.branch.weave_store,
301
 
                                  self.branch.get_transaction())
 
399
                                  self.wt, self.builder)
302
400
        self.assertEqual(self.file_active.revision, '2')
303
401
 
304
402
    def test_snapshot_changed(self):
307
405
        self.file_active.name='newname'
308
406
        rename('subdir/file', 'subdir/newname')
309
407
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
310
 
                                  self.work_tree, 
311
 
                                  self.branch.weave_store,
312
 
                                  self.branch.get_transaction())
 
408
                                  self.wt, self.builder)
313
409
        # expected outcome - file_1 has a revision id of '2'
314
410
        self.assertEqual(self.file_active.revision, '2')
315
411
 
316
412
 
317
 
class TestPreviousHeads(TestCaseInTempDir):
 
413
class TestPreviousHeads(TestCaseWithTransport):
318
414
 
319
415
    def setUp(self):
320
416
        # we want several inventories, that respectively
326
422
        # D) fileid present in two inventories and one is
327
423
        #   a descendent of the other. (B, D)
328
424
        super(TestPreviousHeads, self).setUp()
 
425
        self.wt = self.make_branch_and_tree('.')
 
426
        self.branch = self.wt.branch
329
427
        self.build_tree(['file'])
330
 
        self.branch = Branch.initialize(u'.')
331
 
        self.wt = self.branch.working_tree()
332
428
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
333
 
        self.inv_A = self.branch.get_inventory('A')
334
 
        self.branch.working_tree().add(['file'], ['fileid'])
 
429
        self.inv_A = self.branch.repository.get_inventory('A')
 
430
        self.wt.add(['file'], ['fileid'])
335
431
        self.wt.commit('add file', rev_id='B')
336
 
        self.inv_B = self.branch.get_inventory('B')
337
 
        self.branch.put_controlfile('revision-history', 'A\n')
 
432
        self.inv_B = self.branch.repository.get_inventory('B')
 
433
        uncommit(self.branch, tree=self.wt)
338
434
        self.assertEqual(self.branch.revision_history(), ['A'])
339
435
        self.wt.commit('another add of file', rev_id='C')
340
 
        self.inv_C = self.branch.get_inventory('C')
341
 
        self.wt.add_pending_merge('B')
 
436
        self.inv_C = self.branch.repository.get_inventory('C')
 
437
        self.wt.add_parent_tree_id('B')
342
438
        self.wt.commit('merge in B', rev_id='D')
343
 
        self.inv_D = self.branch.get_inventory('D')
 
439
        self.inv_D = self.branch.repository.get_inventory('D')
344
440
        self.file_active = self.wt.inventory['fileid']
345
 
        self.weave = self.branch.weave_store.get_weave('fileid',
346
 
            self.branch.get_transaction())
 
441
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
442
            self.branch.repository.get_transaction())
347
443
        
348
444
    def get_previous_heads(self, inventories):
349
 
        return self.file_active.find_previous_heads(inventories, self.weave)
 
445
        return self.file_active.find_previous_heads(
 
446
            inventories, 
 
447
            self.branch.repository.weave_store,
 
448
            self.branch.repository.get_transaction())
350
449
        
351
450
    def test_fileid_in_no_inventory(self):
352
451
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
376
475
    # TODO: test two inventories with the same file revision 
377
476
 
378
477
 
379
 
class TestExecutable(TestCaseInTempDir):
380
 
 
381
 
    def test_stays_executable(self):
382
 
        basic_inv = """<inventory format="5">
383
 
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
384
 
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
385
 
</inventory>
386
 
"""
387
 
        os.mkdir('b1')
388
 
        b = Branch.initialize('b1')
 
478
class TestDescribeChanges(TestCase):
 
479
 
 
480
    def test_describe_change(self):
 
481
        # we need to test the following change combinations:
 
482
        # rename
 
483
        # reparent
 
484
        # modify
 
485
        # gone
 
486
        # added
 
487
        # renamed/reparented and modified
 
488
        # change kind (perhaps can't be done yet?)
 
489
        # also, merged in combination with all of these?
 
490
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
491
        old_a.text_sha1 = '123132'
 
492
        old_a.text_size = 0
 
493
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
 
494
        new_a.text_sha1 = '123132'
 
495
        new_a.text_size = 0
 
496
 
 
497
        self.assertChangeDescription('unchanged', old_a, new_a)
 
498
 
 
499
        new_a.text_size = 10
 
500
        new_a.text_sha1 = 'abcabc'
 
501
        self.assertChangeDescription('modified', old_a, new_a)
 
502
 
 
503
        self.assertChangeDescription('added', None, new_a)
 
504
        self.assertChangeDescription('removed', old_a, None)
 
505
        # perhaps a bit questionable but seems like the most reasonable thing...
 
506
        self.assertChangeDescription('unchanged', None, None)
 
507
 
 
508
        # in this case it's both renamed and modified; show a rename and 
 
509
        # modification:
 
510
        new_a.name = 'newfilename'
 
511
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
512
 
 
513
        # reparenting is 'renaming'
 
514
        new_a.name = old_a.name
 
515
        new_a.parent_id = 'somedir-id'
 
516
        self.assertChangeDescription('modified and renamed', old_a, new_a)
 
517
 
 
518
        # reset the content values so its not modified
 
519
        new_a.text_size = old_a.text_size
 
520
        new_a.text_sha1 = old_a.text_sha1
 
521
        new_a.name = old_a.name
 
522
 
 
523
        new_a.name = 'newfilename'
 
524
        self.assertChangeDescription('renamed', old_a, new_a)
 
525
 
 
526
        # reparenting is 'renaming'
 
527
        new_a.name = old_a.name
 
528
        new_a.parent_id = 'somedir-id'
 
529
        self.assertChangeDescription('renamed', old_a, new_a)
 
530
 
 
531
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
 
532
        change = InventoryEntry.describe_change(old_ie, new_ie)
 
533
        self.assertEqual(expected_change, change)
 
534
 
 
535
 
 
536
class TestRevert(TestCaseWithTransport):
 
537
 
 
538
    def test_dangling_id(self):
 
539
        wt = self.make_branch_and_tree('b1')
 
540
        self.assertEqual(len(wt.inventory), 1)
389
541
        open('b1/a', 'wb').write('a test\n')
390
 
        open('b1/b', 'wb').write('b test\n')
391
 
        os.chmod('b1/a', 0755)
392
 
        os.chmod('b1/b', 0644)
393
 
        # Manually writing the inventory, to ensure that
394
 
        # the executable="yes" entry is set for 'a' and not for 'b'
395
 
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
396
 
 
397
 
        a_id = "a-20051208024829-849e76f7968d7a86"
398
 
        b_id = "b-20051208024829-849e76f7968d7a86"
399
 
        t = b.working_tree()
400
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
401
 
 
402
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
403
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
404
 
 
405
 
        t.commit('adding a,b', rev_id='r1')
406
 
 
407
 
        rev_tree = b.revision_tree('r1')
408
 
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
409
 
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
410
 
 
411
 
        self.failUnless(rev_tree.inventory[a_id].executable)
412
 
        self.failIf(rev_tree.inventory[b_id].executable)
413
 
 
414
 
        # Make sure the entries are gone
415
 
        os.remove('b1/a')
416
 
        os.remove('b1/b')
417
 
        self.failIf(t.has_id(a_id))
418
 
        self.failIf(t.has_filename('a'))
419
 
        self.failIf(t.has_id(b_id))
420
 
        self.failIf(t.has_filename('b'))
421
 
 
422
 
        # Make sure that revert is able to bring them back,
423
 
        # and sets 'a' back to being executable
424
 
 
425
 
        t.revert(['b1/a', 'b1/b'], rev_tree, backups=False)
426
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
427
 
 
428
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
429
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
430
 
 
431
 
        # Now remove them again, and make sure that after a
432
 
        # commit, they are still marked correctly
433
 
        os.remove('b1/a')
434
 
        os.remove('b1/b')
435
 
        t.commit('removed', rev_id='r2')
436
 
 
437
 
        self.assertEqual([], [cn for cn,ie in t.inventory.iter_entries()])
438
 
        self.failIf(t.has_id(a_id))
439
 
        self.failIf(t.has_filename('a'))
440
 
        self.failIf(t.has_id(b_id))
441
 
        self.failIf(t.has_filename('b'))
442
 
 
443
 
        # Now revert back to the previous commit
444
 
        t.revert([], rev_tree, backups=False)
445
 
        # TODO: FIXME: For some reason, after revert, the tree does not 
446
 
        # regenerate its working inventory, so we have to manually delete
447
 
        # the working tree, and create a new one
448
 
        # This seems to happen any time you do a merge operation on the
449
 
        # working tree
450
 
        del t
451
 
        t = b.working_tree()
452
 
 
453
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t.inventory.iter_entries()])
454
 
 
455
 
        self.failUnless(t.is_executable(a_id), "'a' lost the execute bit")
456
 
        self.failIf(t.is_executable(b_id), "'b' gained an execute bit")
457
 
 
458
 
        # Now make sure that 'bzr branch' also preserves the
459
 
        # executable bit
460
 
        # TODO: Maybe this should be a blackbox test
461
 
        from bzrlib.clone import copy_branch
462
 
        copy_branch(b, 'b2', revision='r1')
463
 
        b2 = Branch.open('b2')
464
 
        self.assertEquals('r1', b2.last_revision())
465
 
        t2 = b2.working_tree()
466
 
 
467
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
468
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
469
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
470
 
 
471
 
        # Make sure pull will delete the files
472
 
        t2.pull(b)
473
 
        self.assertEquals('r2', b2.last_revision())
474
 
        # FIXME: Same thing here, t2 needs to be recreated
475
 
        del t2
476
 
        t2 = b2.working_tree()
477
 
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
478
 
 
479
 
        # Now commit the changes on the first branch
480
 
        # so that the second branch can pull the changes
481
 
        # and make sure that the executable bit has been copied
482
 
        t.commit('resurrected', rev_id='r3')
483
 
 
484
 
        t2.pull(b)
485
 
        # FIXME: And here
486
 
        del t2
487
 
        t2 = b2.working_tree()
488
 
        self.assertEquals('r3', b2.last_revision())
489
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
490
 
 
491
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
492
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
542
        wt.add('a')
 
543
        self.assertEqual(len(wt.inventory), 2)
 
544
        os.unlink('b1/a')
 
545
        wt.revert([])
 
546
        self.assertEqual(len(wt.inventory), 1)