/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/workingtree_implementations/test_workingtree.py

  • Committer: Aaron Bentley
  • Date: 2008-10-16 21:27:10 UTC
  • mfrom: (0.15.26 unshelve)
  • mto: (0.16.74 shelf-ui)
  • mto: This revision was merged to the branch mainline in revision 3820.
  • Revision ID: aaron@aaronbentley.com-20081016212710-h9av3nhk76dvmsv5
Merge with unshelve

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
# Authors:  Robert Collins <robert.collins@canonical.com>
 
3
#           and others
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 
 
19
from cStringIO import StringIO
 
20
import errno
 
21
import os
 
22
import sys
 
23
 
 
24
from bzrlib import (
 
25
    branch,
 
26
    bzrdir,
 
27
    errors,
 
28
    osutils,
 
29
    tests,
 
30
    urlutils,
 
31
    workingtree,
 
32
    )
 
33
from bzrlib.errors import (NotBranchError, NotVersionedError,
 
34
                           UnsupportedOperation, PathsNotVersionedError)
 
35
from bzrlib.inventory import Inventory
 
36
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
 
37
from bzrlib.tests import TestSkipped, TestNotApplicable
 
38
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
 
39
from bzrlib.trace import mutter
 
40
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
 
41
                                WorkingTree, WorkingTree2)
 
42
from bzrlib.conflicts import ConflictList, TextConflict, ContentsConflict
 
43
 
 
44
 
 
45
class TestWorkingTree(TestCaseWithWorkingTree):
 
46
 
 
47
    def test_list_files(self):
 
48
        tree = self.make_branch_and_tree('.')
 
49
        self.build_tree(['dir/', 'file'])
 
50
        if has_symlinks():
 
51
            os.symlink('target', 'symlink')
 
52
        tree.lock_read()
 
53
        files = list(tree.list_files())
 
54
        tree.unlock()
 
55
        self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
 
56
        self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
 
57
        if has_symlinks():
 
58
            self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
 
59
 
 
60
    def test_list_files_sorted(self):
 
61
        tree = self.make_branch_and_tree('.')
 
62
        self.build_tree(['dir/', 'file', 'dir/file', 'dir/b',
 
63
                         'dir/subdir/', 'a', 'dir/subfile',
 
64
                         'zz_dir/', 'zz_dir/subfile'])
 
65
        tree.lock_read()
 
66
        files = [(path, kind) for (path, v, kind, file_id, entry)
 
67
                               in tree.list_files()]
 
68
        tree.unlock()
 
69
        self.assertEqual([
 
70
            ('a', 'file'),
 
71
            ('dir', 'directory'),
 
72
            ('file', 'file'),
 
73
            ('zz_dir', 'directory'),
 
74
            ], files)
 
75
 
 
76
        tree.add(['dir', 'zz_dir'])
 
77
        tree.lock_read()
 
78
        files = [(path, kind) for (path, v, kind, file_id, entry)
 
79
                               in tree.list_files()]
 
80
        tree.unlock()
 
81
        self.assertEqual([
 
82
            ('a', 'file'),
 
83
            ('dir', 'directory'),
 
84
            ('dir/b', 'file'),
 
85
            ('dir/file', 'file'),
 
86
            ('dir/subdir', 'directory'),
 
87
            ('dir/subfile', 'file'),
 
88
            ('file', 'file'),
 
89
            ('zz_dir', 'directory'),
 
90
            ('zz_dir/subfile', 'file'),
 
91
            ], files)
 
92
 
 
93
    def test_list_files_kind_change(self):
 
94
        tree = self.make_branch_and_tree('tree')
 
95
        self.build_tree(['tree/filename'])
 
96
        tree.add('filename', 'file-id')
 
97
        os.unlink('tree/filename')
 
98
        self.build_tree(['tree/filename/'])
 
99
        tree.lock_read()
 
100
        self.addCleanup(tree.unlock)
 
101
        result = list(tree.list_files())
 
102
        self.assertEqual(1, len(result))
 
103
        self.assertEqual(('filename', 'V', 'directory', 'file-id'),
 
104
                         result[0][:4])
 
105
 
 
106
    def test_open_containing(self):
 
107
        branch = self.make_branch_and_tree('.').branch
 
108
        local_base = urlutils.local_path_from_url(branch.base)
 
109
 
 
110
        # Empty opens '.'
 
111
        wt, relpath = WorkingTree.open_containing()
 
112
        self.assertEqual('', relpath)
 
113
        self.assertEqual(wt.basedir + '/', local_base)
 
114
 
 
115
        # '.' opens this dir
 
116
        wt, relpath = WorkingTree.open_containing(u'.')
 
117
        self.assertEqual('', relpath)
 
118
        self.assertEqual(wt.basedir + '/', local_base)
 
119
 
 
120
        # './foo' finds '.' and a relpath of 'foo'
 
121
        wt, relpath = WorkingTree.open_containing('./foo')
 
122
        self.assertEqual('foo', relpath)
 
123
        self.assertEqual(wt.basedir + '/', local_base)
 
124
 
 
125
        # abspath(foo) finds '.' and relpath of 'foo'
 
126
        wt, relpath = WorkingTree.open_containing('./foo')
 
127
        wt, relpath = WorkingTree.open_containing(getcwd() + '/foo')
 
128
        self.assertEqual('foo', relpath)
 
129
        self.assertEqual(wt.basedir + '/', local_base)
 
130
 
 
131
        # can even be a url: finds '.' and relpath of 'foo'
 
132
        wt, relpath = WorkingTree.open_containing('./foo')
 
133
        wt, relpath = WorkingTree.open_containing(
 
134
                    urlutils.local_path_to_url(getcwd() + '/foo'))
 
135
        self.assertEqual('foo', relpath)
 
136
        self.assertEqual(wt.basedir + '/', local_base)
 
137
 
 
138
    def test_basic_relpath(self):
 
139
        # for comprehensive relpath tests, see whitebox.py.
 
140
        tree = self.make_branch_and_tree('.')
 
141
        self.assertEqual('child',
 
142
                         tree.relpath(pathjoin(getcwd(), 'child')))
 
143
 
 
144
    def test_lock_locks_branch(self):
 
145
        tree = self.make_branch_and_tree('.')
 
146
        tree.lock_read()
 
147
        self.assertEqual('r', tree.branch.peek_lock_mode())
 
148
        tree.unlock()
 
149
        self.assertEqual(None, tree.branch.peek_lock_mode())
 
150
        tree.lock_write()
 
151
        self.assertEqual('w', tree.branch.peek_lock_mode())
 
152
        tree.unlock()
 
153
        self.assertEqual(None, tree.branch.peek_lock_mode())
 
154
 
 
155
    def test_revert(self):
 
156
        """Test selected-file revert"""
 
157
        tree = self.make_branch_and_tree('.')
 
158
 
 
159
        self.build_tree(['hello.txt'])
 
160
        file('hello.txt', 'w').write('initial hello')
 
161
 
 
162
        self.assertRaises(PathsNotVersionedError,
 
163
                          tree.revert, ['hello.txt'])
 
164
        tree.add(['hello.txt'])
 
165
        tree.commit('create initial hello.txt')
 
166
 
 
167
        self.check_file_contents('hello.txt', 'initial hello')
 
168
        file('hello.txt', 'w').write('new hello')
 
169
        self.check_file_contents('hello.txt', 'new hello')
 
170
 
 
171
        # revert file modified since last revision
 
172
        tree.revert(['hello.txt'])
 
173
        self.check_file_contents('hello.txt', 'initial hello')
 
174
        self.check_file_contents('hello.txt.~1~', 'new hello')
 
175
 
 
176
        # reverting again does not clobber the backup
 
177
        tree.revert(['hello.txt'])
 
178
        self.check_file_contents('hello.txt', 'initial hello')
 
179
        self.check_file_contents('hello.txt.~1~', 'new hello')
 
180
        
 
181
        # backup files are numbered
 
182
        file('hello.txt', 'w').write('new hello2')
 
183
        tree.revert(['hello.txt'])
 
184
        self.check_file_contents('hello.txt', 'initial hello')
 
185
        self.check_file_contents('hello.txt.~1~', 'new hello')
 
186
        self.check_file_contents('hello.txt.~2~', 'new hello2')
 
187
 
 
188
    def test_revert_missing(self):
 
189
        # Revert a file that has been deleted since last commit
 
190
        tree = self.make_branch_and_tree('.')
 
191
        file('hello.txt', 'w').write('initial hello')
 
192
        tree.add('hello.txt')
 
193
        tree.commit('added hello.txt')
 
194
        os.unlink('hello.txt')
 
195
        tree.remove('hello.txt')
 
196
        tree.revert(['hello.txt'])
 
197
        self.failUnlessExists('hello.txt')
 
198
 
 
199
    def test_versioned_files_not_unknown(self):
 
200
        tree = self.make_branch_and_tree('.')
 
201
        self.build_tree(['hello.txt'])
 
202
        tree.add('hello.txt')
 
203
        self.assertEquals(list(tree.unknowns()),
 
204
                          [])
 
205
 
 
206
    def test_unknowns(self):
 
207
        tree = self.make_branch_and_tree('.')
 
208
        self.build_tree(['hello.txt',
 
209
                         'hello.txt.~1~'])
 
210
        self.build_tree_contents([('.bzrignore', '*.~*\n')])
 
211
        tree.add('.bzrignore')
 
212
        self.assertEquals(list(tree.unknowns()),
 
213
                          ['hello.txt'])
 
214
 
 
215
    def test_initialize(self):
 
216
        # initialize should create a working tree and branch in an existing dir
 
217
        t = self.make_branch_and_tree('.')
 
218
        b = branch.Branch.open('.')
 
219
        self.assertEqual(t.branch.base, b.base)
 
220
        t2 = WorkingTree.open('.')
 
221
        self.assertEqual(t.basedir, t2.basedir)
 
222
        self.assertEqual(b.base, t2.branch.base)
 
223
        # TODO maybe we should check the branch format? not sure if its
 
224
        # appropriate here.
 
225
 
 
226
    def test_rename_dirs(self):
 
227
        """Test renaming directories and the files within them."""
 
228
        wt = self.make_branch_and_tree('.')
 
229
        b = wt.branch
 
230
        self.build_tree(['dir/', 'dir/sub/', 'dir/sub/file'])
 
231
        wt.add(['dir', 'dir/sub', 'dir/sub/file'])
 
232
 
 
233
        wt.commit('create initial state')
 
234
 
 
235
        revid = b.revision_history()[0]
 
236
        self.log('first revision_id is {%s}' % revid)
 
237
        
 
238
        inv = b.repository.get_revision_inventory(revid)
 
239
        self.log('contents of inventory: %r' % inv.entries())
 
240
 
 
241
        self.check_inventory_shape(inv,
 
242
                                   ['dir/', 'dir/sub/', 'dir/sub/file'])
 
243
        wt.rename_one('dir', 'newdir')
 
244
 
 
245
        wt.lock_read()
 
246
        self.check_inventory_shape(wt.inventory,
 
247
                                   ['newdir/', 'newdir/sub/', 'newdir/sub/file'])
 
248
        wt.unlock()
 
249
        wt.rename_one('newdir/sub', 'newdir/newsub')
 
250
        wt.lock_read()
 
251
        self.check_inventory_shape(wt.inventory,
 
252
                                   ['newdir/', 'newdir/newsub/',
 
253
                                    'newdir/newsub/file'])
 
254
        wt.unlock()
 
255
 
 
256
    def test_add_in_unversioned(self):
 
257
        """Try to add a file in an unversioned directory.
 
258
 
 
259
        "bzr add" adds the parent as necessary, but simple working tree add
 
260
        doesn't do that.
 
261
        """
 
262
        from bzrlib.errors import NotVersionedError
 
263
        wt = self.make_branch_and_tree('.')
 
264
        self.build_tree(['foo/',
 
265
                         'foo/hello'])
 
266
        self.assertRaises(NotVersionedError,
 
267
                          wt.add,
 
268
                          'foo/hello')
 
269
 
 
270
    def test_add_missing(self):
 
271
        # adding a msising file -> NoSuchFile
 
272
        wt = self.make_branch_and_tree('.')
 
273
        self.assertRaises(errors.NoSuchFile, wt.add, 'fpp')
 
274
 
 
275
    def test_remove_verbose(self):
 
276
        #FIXME the remove api should not print or otherwise depend on the
 
277
        # text UI - RBC 20060124
 
278
        wt = self.make_branch_and_tree('.')
 
279
        self.build_tree(['hello'])
 
280
        wt.add(['hello'])
 
281
        wt.commit(message='add hello')
 
282
        stdout = StringIO()
 
283
        stderr = StringIO()
 
284
        self.assertEqual(None, self.apply_redirected(None, stdout, stderr,
 
285
                                                     wt.remove,
 
286
                                                     ['hello'],
 
287
                                                     verbose=True))
 
288
        self.assertEqual('?       hello\n', stdout.getvalue())
 
289
        self.assertEqual('', stderr.getvalue())
 
290
 
 
291
    def test_clone_trivial(self):
 
292
        wt = self.make_branch_and_tree('source')
 
293
        cloned_dir = wt.bzrdir.clone('target')
 
294
        cloned = cloned_dir.open_workingtree()
 
295
        self.assertEqual(cloned.get_parent_ids(), wt.get_parent_ids())
 
296
 
 
297
    def test_last_revision(self):
 
298
        wt = self.make_branch_and_tree('source')
 
299
        self.assertEqual([], wt.get_parent_ids())
 
300
        wt.commit('A', allow_pointless=True, rev_id='A')
 
301
        parent_ids = wt.get_parent_ids()
 
302
        self.assertEqual(['A'], parent_ids)
 
303
        for parent_id in parent_ids:
 
304
            self.assertIsInstance(parent_id, str)
 
305
 
 
306
    def test_set_last_revision(self):
 
307
        wt = self.make_branch_and_tree('source')
 
308
        # set last-revision to one not in the history
 
309
        wt.set_last_revision('A')
 
310
        # set it back to None for an empty tree.
 
311
        wt.set_last_revision('null:')
 
312
        wt.commit('A', allow_pointless=True, rev_id='A')
 
313
        self.assertEqual(['A'], wt.get_parent_ids())
 
314
        # None is aways in the branch
 
315
        wt.set_last_revision('null:')
 
316
        self.assertEqual([], wt.get_parent_ids())
 
317
        # and now we can set it to 'A'
 
318
        # because some formats mutate the branch to set it on the tree
 
319
        # we need to alter the branch to let this pass.
 
320
        try:
 
321
            wt.branch.set_revision_history(['A', 'B'])
 
322
        except errors.NoSuchRevision, e:
 
323
            self.assertEqual('B', e.revision)
 
324
            raise TestSkipped("Branch format does not permit arbitrary"
 
325
                              " history")
 
326
        wt.set_last_revision('A')
 
327
        self.assertEqual(['A'], wt.get_parent_ids())
 
328
        self.assertRaises(errors.ReservedId, wt.set_last_revision, 'A:')
 
329
 
 
330
    def test_set_last_revision_different_to_branch(self):
 
331
        # working tree formats from the meta-dir format and newer support
 
332
        # setting the last revision on a tree independently of that on the 
 
333
        # branch. Its concievable that some future formats may want to 
 
334
        # couple them again (i.e. because its really a smart server and
 
335
        # the working tree will always match the branch). So we test
 
336
        # that formats where initialising a branch does not initialise a 
 
337
        # tree - and thus have separable entities - support skewing the 
 
338
        # two things.
 
339
        branch = self.make_branch('tree')
 
340
        try:
 
341
            # if there is a working tree now, this is not supported.
 
342
            branch.bzrdir.open_workingtree()
 
343
            return
 
344
        except errors.NoWorkingTree:
 
345
            pass
 
346
        wt = branch.bzrdir.create_workingtree()
 
347
        wt.commit('A', allow_pointless=True, rev_id='A')
 
348
        wt.set_last_revision(None)
 
349
        self.assertEqual([], wt.get_parent_ids())
 
350
        self.assertEqual('A', wt.branch.last_revision())
 
351
        # and now we can set it back to 'A'
 
352
        wt.set_last_revision('A')
 
353
        self.assertEqual(['A'], wt.get_parent_ids())
 
354
        self.assertEqual('A', wt.branch.last_revision())
 
355
 
 
356
    def test_clone_and_commit_preserves_last_revision(self):
 
357
        """Doing a commit into a clone tree does not affect the source."""
 
358
        wt = self.make_branch_and_tree('source')
 
359
        cloned_dir = wt.bzrdir.clone('target')
 
360
        wt.commit('A', allow_pointless=True, rev_id='A')
 
361
        self.assertNotEqual(cloned_dir.open_workingtree().get_parent_ids(),
 
362
                            wt.get_parent_ids())
 
363
 
 
364
    def test_clone_preserves_content(self):
 
365
        wt = self.make_branch_and_tree('source')
 
366
        self.build_tree(['added', 'deleted', 'notadded'],
 
367
                        transport=wt.bzrdir.transport.clone('..'))
 
368
        wt.add('deleted', 'deleted')
 
369
        wt.commit('add deleted')
 
370
        wt.remove('deleted')
 
371
        wt.add('added', 'added')
 
372
        cloned_dir = wt.bzrdir.clone('target')
 
373
        cloned = cloned_dir.open_workingtree()
 
374
        cloned_transport = cloned.bzrdir.transport.clone('..')
 
375
        self.assertFalse(cloned_transport.has('deleted'))
 
376
        self.assertTrue(cloned_transport.has('added'))
 
377
        self.assertFalse(cloned_transport.has('notadded'))
 
378
        self.assertEqual('added', cloned.path2id('added'))
 
379
        self.assertEqual(None, cloned.path2id('deleted'))
 
380
        self.assertEqual(None, cloned.path2id('notadded'))
 
381
        
 
382
    def test_basis_tree_returns_last_revision(self):
 
383
        wt = self.make_branch_and_tree('.')
 
384
        self.build_tree(['foo'])
 
385
        wt.add('foo', 'foo-id')
 
386
        wt.commit('A', rev_id='A')
 
387
        wt.rename_one('foo', 'bar')
 
388
        wt.commit('B', rev_id='B')
 
389
        wt.set_parent_ids(['B'])
 
390
        tree = wt.basis_tree()
 
391
        tree.lock_read()
 
392
        self.failUnless(tree.has_filename('bar'))
 
393
        tree.unlock()
 
394
        wt.set_parent_ids(['A'])
 
395
        tree = wt.basis_tree()
 
396
        tree.lock_read()
 
397
        self.failUnless(tree.has_filename('foo'))
 
398
        tree.unlock()
 
399
 
 
400
    def test_clone_tree_revision(self):
 
401
        # make a tree with a last-revision,
 
402
        # and clone it with a different last-revision, this should switch
 
403
        # do it.
 
404
        #
 
405
        # also test that the content is merged
 
406
        # and conflicts recorded.
 
407
        # This should merge between the trees - local edits should be preserved
 
408
        # but other changes occured.
 
409
        # we test this by having one file that does
 
410
        # not change between two revisions, and another that does -
 
411
        # if the changed one is not changed, fail,
 
412
        # if the one that did not change has lost a local change, fail.
 
413
        # 
 
414
        raise TestSkipped('revision limiting is not implemented yet.')
 
415
 
 
416
    def test_initialize_with_revision_id(self):
 
417
        # a bzrdir can construct a working tree for itself @ a specific revision.
 
418
        source = self.make_branch_and_tree('source')
 
419
        source.commit('a', rev_id='a', allow_pointless=True)
 
420
        source.commit('b', rev_id='b', allow_pointless=True)
 
421
        self.build_tree(['new/'])
 
422
        made_control = self.bzrdir_format.initialize('new')
 
423
        source.branch.repository.clone(made_control)
 
424
        source.branch.clone(made_control)
 
425
        made_tree = self.workingtree_format.initialize(made_control, revision_id='a')
 
426
        self.assertEqual(['a'], made_tree.get_parent_ids())
 
427
 
 
428
    def test_update_sets_last_revision(self):
 
429
        # working tree formats from the meta-dir format and newer support
 
430
        # setting the last revision on a tree independently of that on the 
 
431
        # branch. Its concievable that some future formats may want to 
 
432
        # couple them again (i.e. because its really a smart server and
 
433
        # the working tree will always match the branch). So we test
 
434
        # that formats where initialising a branch does not initialise a 
 
435
        # tree - and thus have separable entities - support skewing the 
 
436
        # two things.
 
437
        main_branch = self.make_branch('tree')
 
438
        try:
 
439
            # if there is a working tree now, this is not supported.
 
440
            main_branch.bzrdir.open_workingtree()
 
441
            return
 
442
        except errors.NoWorkingTree:
 
443
            pass
 
444
        wt = main_branch.bzrdir.create_workingtree()
 
445
        # create an out of date working tree by making a checkout in this
 
446
        # current format
 
447
        self.build_tree(['checkout/', 'tree/file'])
 
448
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
449
        branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
450
        old_tree = self.workingtree_format.initialize(checkout)
 
451
        # now commit to 'tree'
 
452
        wt.add('file')
 
453
        wt.commit('A', rev_id='A')
 
454
        # and update old_tree
 
455
        self.assertEqual(0, old_tree.update())
 
456
        self.failUnlessExists('checkout/file')
 
457
        self.assertEqual(['A'], old_tree.get_parent_ids())
 
458
 
 
459
    def test_update_sets_root_id(self):
 
460
        """Ensure tree root is set properly by update.
 
461
        
 
462
        Since empty trees don't have root_ids, but workingtrees do,
 
463
        an update of a checkout of revision 0 to a new revision,  should set
 
464
        the root id.
 
465
        """
 
466
        wt = self.make_branch_and_tree('tree')
 
467
        main_branch = wt.branch
 
468
        # create an out of date working tree by making a checkout in this
 
469
        # current format
 
470
        self.build_tree(['checkout/', 'tree/file'])
 
471
        checkout = main_branch.create_checkout('checkout')
 
472
        # now commit to 'tree'
 
473
        wt.add('file')
 
474
        wt.commit('A', rev_id='A')
 
475
        # and update checkout 
 
476
        self.assertEqual(0, checkout.update())
 
477
        self.failUnlessExists('checkout/file')
 
478
        self.assertEqual(wt.get_root_id(), checkout.get_root_id())
 
479
        self.assertNotEqual(None, wt.get_root_id())
 
480
 
 
481
    def test_update_returns_conflict_count(self):
 
482
        # working tree formats from the meta-dir format and newer support
 
483
        # setting the last revision on a tree independently of that on the 
 
484
        # branch. Its concievable that some future formats may want to 
 
485
        # couple them again (i.e. because its really a smart server and
 
486
        # the working tree will always match the branch). So we test
 
487
        # that formats where initialising a branch does not initialise a 
 
488
        # tree - and thus have separable entities - support skewing the 
 
489
        # two things.
 
490
        main_branch = self.make_branch('tree')
 
491
        try:
 
492
            # if there is a working tree now, this is not supported.
 
493
            main_branch.bzrdir.open_workingtree()
 
494
            return
 
495
        except errors.NoWorkingTree:
 
496
            pass
 
497
        wt = main_branch.bzrdir.create_workingtree()
 
498
        # create an out of date working tree by making a checkout in this
 
499
        # current format
 
500
        self.build_tree(['checkout/', 'tree/file'])
 
501
        checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
 
502
        branch.BranchReferenceFormat().initialize(checkout, main_branch)
 
503
        old_tree = self.workingtree_format.initialize(checkout)
 
504
        # now commit to 'tree'
 
505
        wt.add('file')
 
506
        wt.commit('A', rev_id='A')
 
507
        # and add a file file to the checkout
 
508
        self.build_tree(['checkout/file'])
 
509
        old_tree.add('file')
 
510
        # and update old_tree
 
511
        self.assertEqual(1, old_tree.update())
 
512
        self.assertEqual(['A'], old_tree.get_parent_ids())
 
513
 
 
514
    def test_merge_revert(self):
 
515
        from bzrlib.merge import merge_inner
 
516
        this = self.make_branch_and_tree('b1')
 
517
        open('b1/a', 'wb').write('a test\n')
 
518
        this.add('a')
 
519
        open('b1/b', 'wb').write('b test\n')
 
520
        this.add('b')
 
521
        this.commit(message='')
 
522
        base = this.bzrdir.clone('b2').open_workingtree()
 
523
        open('b2/a', 'wb').write('b test\n')
 
524
        other = this.bzrdir.clone('b3').open_workingtree()
 
525
        open('b3/a', 'wb').write('c test\n')
 
526
        open('b3/c', 'wb').write('c test\n')
 
527
        other.add('c')
 
528
 
 
529
        open('b1/b', 'wb').write('q test\n')
 
530
        open('b1/d', 'wb').write('d test\n')
 
531
        merge_inner(this.branch, other, base, this_tree=this)
 
532
        self.assertNotEqual(open('b1/a', 'rb').read(), 'a test\n')
 
533
        this.revert()
 
534
        self.assertEqual(open('b1/a', 'rb').read(), 'a test\n')
 
535
        self.assertIs(os.path.exists('b1/b.~1~'), True)
 
536
        self.assertIs(os.path.exists('b1/c'), False)
 
537
        self.assertIs(os.path.exists('b1/a.~1~'), False)
 
538
        self.assertIs(os.path.exists('b1/d'), True)
 
539
 
 
540
    def test_update_updates_bound_branch_no_local_commits(self):
 
541
        # doing an update in a tree updates the branch its bound to too.
 
542
        master_tree = self.make_branch_and_tree('master')
 
543
        tree = self.make_branch_and_tree('tree')
 
544
        try:
 
545
            tree.branch.bind(master_tree.branch)
 
546
        except errors.UpgradeRequired:
 
547
            # legacy branches cannot bind
 
548
            return
 
549
        master_tree.commit('foo', rev_id='foo', allow_pointless=True)
 
550
        tree.update()
 
551
        self.assertEqual(['foo'], tree.get_parent_ids())
 
552
        self.assertEqual('foo', tree.branch.last_revision())
 
553
 
 
554
    def test_update_turns_local_commit_into_merge(self):
 
555
        # doing an update with a few local commits and no master commits
 
556
        # makes pending-merges. 
 
557
        # this is done so that 'bzr update; bzr revert' will always produce
 
558
        # an exact copy of the 'logical branch' - the referenced branch for
 
559
        # a checkout, and the master for a bound branch.
 
560
        # its possible that we should instead have 'bzr update' when there
 
561
        # is nothing new on the master leave the current commits intact and
 
562
        # alter 'revert' to revert to the master always. But for now, its
 
563
        # good.
 
564
        master_tree = self.make_branch_and_tree('master')
 
565
        master_tip = master_tree.commit('first master commit')
 
566
        tree = self.make_branch_and_tree('tree')
 
567
        try:
 
568
            tree.branch.bind(master_tree.branch)
 
569
        except errors.UpgradeRequired:
 
570
            # legacy branches cannot bind
 
571
            return
 
572
        # sync with master
 
573
        tree.update()
 
574
        # work locally
 
575
        tree.commit('foo', rev_id='foo', allow_pointless=True, local=True)
 
576
        tree.commit('bar', rev_id='bar', allow_pointless=True, local=True)
 
577
        # sync with master prepatory to committing
 
578
        tree.update()
 
579
        # which should have pivoted the local tip into a merge
 
580
        self.assertEqual([master_tip, 'bar'], tree.get_parent_ids())
 
581
        # and the local branch history should match the masters now.
 
582
        self.assertEqual(master_tree.branch.revision_history(),
 
583
            tree.branch.revision_history())
 
584
 
 
585
    def test_merge_modified_detects_corruption(self):
 
586
        # FIXME: This doesn't really test that it works; also this is not
 
587
        # implementation-independent. mbp 20070226
 
588
        tree = self.make_branch_and_tree('master')
 
589
        tree._transport.put_bytes('merge-hashes', 'asdfasdf')
 
590
        self.assertRaises(errors.MergeModifiedFormatError, tree.merge_modified)
 
591
 
 
592
    def test_merge_modified(self):
 
593
        # merge_modified stores a map from file id to hash
 
594
        tree = self.make_branch_and_tree('tree')
 
595
        d = {'file-id': osutils.sha_string('hello')}
 
596
        self.build_tree_contents([('tree/somefile', 'hello')])
 
597
        tree.lock_write()
 
598
        try:
 
599
            tree.add(['somefile'], ['file-id'])
 
600
            tree.set_merge_modified(d)
 
601
            mm = tree.merge_modified()
 
602
            self.assertEquals(mm, d)
 
603
        finally:
 
604
            tree.unlock()
 
605
        mm = tree.merge_modified()
 
606
        self.assertEquals(mm, d)
 
607
 
 
608
    def test_conflicts(self):
 
609
        from bzrlib.tests.test_conflicts import example_conflicts
 
610
        tree = self.make_branch_and_tree('master')
 
611
        try:
 
612
            tree.set_conflicts(example_conflicts)
 
613
        except UnsupportedOperation:
 
614
            raise TestSkipped('set_conflicts not supported')
 
615
            
 
616
        tree2 = WorkingTree.open('master')
 
617
        self.assertEqual(tree2.conflicts(), example_conflicts)
 
618
        tree2._transport.put_bytes('conflicts', '')
 
619
        self.assertRaises(errors.ConflictFormatError,
 
620
                          tree2.conflicts)
 
621
        tree2._transport.put_bytes('conflicts', 'a')
 
622
        self.assertRaises(errors.ConflictFormatError,
 
623
                          tree2.conflicts)
 
624
 
 
625
    def make_merge_conflicts(self):
 
626
        from bzrlib.merge import merge_inner
 
627
        tree = self.make_branch_and_tree('mine')
 
628
        file('mine/bloo', 'wb').write('one')
 
629
        file('mine/blo', 'wb').write('on')
 
630
        tree.add(['bloo', 'blo'])
 
631
        tree.commit("blah", allow_pointless=False)
 
632
        base = tree.branch.repository.revision_tree(tree.last_revision())
 
633
        bzrdir.BzrDir.open("mine").sprout("other")
 
634
        file('other/bloo', 'wb').write('two')
 
635
        othertree = WorkingTree.open('other')
 
636
        othertree.commit('blah', allow_pointless=False)
 
637
        file('mine/bloo', 'wb').write('three')
 
638
        tree.commit("blah", allow_pointless=False)
 
639
        merge_inner(tree.branch, othertree, base, this_tree=tree)
 
640
        return tree
 
641
 
 
642
    def test_merge_conflicts(self):
 
643
        tree = self.make_merge_conflicts()
 
644
        self.assertEqual(len(tree.conflicts()), 1)
 
645
 
 
646
    def test_clear_merge_conflicts(self):
 
647
        tree = self.make_merge_conflicts()
 
648
        self.assertEqual(len(tree.conflicts()), 1)
 
649
        try:
 
650
            tree.set_conflicts(ConflictList())
 
651
        except UnsupportedOperation:
 
652
            raise TestSkipped('unsupported operation')
 
653
        self.assertEqual(tree.conflicts(), ConflictList())
 
654
 
 
655
    def test_add_conflicts(self):
 
656
        tree = self.make_branch_and_tree('tree')
 
657
        try:
 
658
            tree.add_conflicts([TextConflict('path_a')])
 
659
        except UnsupportedOperation:
 
660
            raise TestSkipped('unsupported operation')
 
661
        self.assertEqual(ConflictList([TextConflict('path_a')]),
 
662
                         tree.conflicts())
 
663
        tree.add_conflicts([TextConflict('path_a')])
 
664
        self.assertEqual(ConflictList([TextConflict('path_a')]), 
 
665
                         tree.conflicts())
 
666
        tree.add_conflicts([ContentsConflict('path_a')])
 
667
        self.assertEqual(ConflictList([ContentsConflict('path_a'), 
 
668
                                       TextConflict('path_a')]),
 
669
                         tree.conflicts())
 
670
        tree.add_conflicts([TextConflict('path_b')])
 
671
        self.assertEqual(ConflictList([ContentsConflict('path_a'), 
 
672
                                       TextConflict('path_a'),
 
673
                                       TextConflict('path_b')]),
 
674
                         tree.conflicts())
 
675
 
 
676
    def test_revert_clear_conflicts(self):
 
677
        tree = self.make_merge_conflicts()
 
678
        self.assertEqual(len(tree.conflicts()), 1)
 
679
        tree.revert(["blo"])
 
680
        self.assertEqual(len(tree.conflicts()), 1)
 
681
        tree.revert(["bloo"])
 
682
        self.assertEqual(len(tree.conflicts()), 0)
 
683
 
 
684
    def test_revert_clear_conflicts2(self):
 
685
        tree = self.make_merge_conflicts()
 
686
        self.assertEqual(len(tree.conflicts()), 1)
 
687
        tree.revert()
 
688
        self.assertEqual(len(tree.conflicts()), 0)
 
689
 
 
690
    def test_format_description(self):
 
691
        tree = self.make_branch_and_tree('tree')
 
692
        text = tree._format.get_format_description()
 
693
        self.failUnless(len(text))
 
694
 
 
695
    def test_branch_attribute_is_not_settable(self):
 
696
        # the branch attribute is an aspect of the working tree, not a
 
697
        # configurable attribute
 
698
        tree = self.make_branch_and_tree('tree')
 
699
        def set_branch():
 
700
            tree.branch = tree.branch
 
701
        self.assertRaises(AttributeError, set_branch)
 
702
 
 
703
    def test_list_files_versioned_before_ignored(self):
 
704
        """A versioned file matching an ignore rule should not be ignored."""
 
705
        tree = self.make_branch_and_tree('.')
 
706
        self.build_tree(['foo.pyc'])
 
707
        # ensure that foo.pyc is ignored
 
708
        self.build_tree_contents([('.bzrignore', 'foo.pyc')])
 
709
        tree.add('foo.pyc', 'anid')
 
710
        tree.lock_read()
 
711
        files = sorted(list(tree.list_files()))
 
712
        tree.unlock()
 
713
        self.assertEqual((u'.bzrignore', '?', 'file', None), files[0][:-1])
 
714
        self.assertEqual((u'foo.pyc', 'V', 'file', 'anid'), files[1][:-1])
 
715
        self.assertEqual(2, len(files))
 
716
 
 
717
    def test_non_normalized_add_accessible(self):
 
718
        try:
 
719
            self.build_tree([u'a\u030a'])
 
720
        except UnicodeError:
 
721
            raise TestSkipped('Filesystem does not support unicode filenames')
 
722
        tree = self.make_branch_and_tree('.')
 
723
        orig = osutils.normalized_filename
 
724
        osutils.normalized_filename = osutils._accessible_normalized_filename
 
725
        try:
 
726
            tree.add([u'a\u030a'])
 
727
            tree.lock_read()
 
728
            self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
 
729
                    [(path, ie.kind) for path,ie in 
 
730
                                tree.inventory.iter_entries()])
 
731
            tree.unlock()
 
732
        finally:
 
733
            osutils.normalized_filename = orig
 
734
 
 
735
    def test_non_normalized_add_inaccessible(self):
 
736
        try:
 
737
            self.build_tree([u'a\u030a'])
 
738
        except UnicodeError:
 
739
            raise TestSkipped('Filesystem does not support unicode filenames')
 
740
        tree = self.make_branch_and_tree('.')
 
741
        orig = osutils.normalized_filename
 
742
        osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
743
        try:
 
744
            self.assertRaises(errors.InvalidNormalization,
 
745
                tree.add, [u'a\u030a'])
 
746
        finally:
 
747
            osutils.normalized_filename = orig
 
748
 
 
749
    def test__write_inventory(self):
 
750
        # The private interface _write_inventory is currently used by transform.
 
751
        tree = self.make_branch_and_tree('.')
 
752
        # if we write write an inventory then do a walkdirs we should get back
 
753
        # missing entries, and actual, and unknowns as appropriate.
 
754
        self.build_tree(['present', 'unknown'])
 
755
        inventory = Inventory(tree.get_root_id())
 
756
        inventory.add_path('missing', 'file', 'missing-id')
 
757
        inventory.add_path('present', 'file', 'present-id')
 
758
        # there is no point in being able to write an inventory to an unlocked
 
759
        # tree object - its a low level api not a convenience api.
 
760
        tree.lock_write()
 
761
        tree._write_inventory(inventory)
 
762
        tree.unlock()
 
763
        tree.lock_read()
 
764
        try:
 
765
            present_stat = os.lstat('present')
 
766
            unknown_stat = os.lstat('unknown')
 
767
            expected_results = [
 
768
                (('', tree.get_root_id()),
 
769
                 [('missing', 'missing', 'unknown', None, 'missing-id', 'file'),
 
770
                  ('present', 'present', 'file', present_stat, 'present-id', 'file'),
 
771
                  ('unknown', 'unknown', 'file', unknown_stat, None, None),
 
772
                 ]
 
773
                )]
 
774
            self.assertEqual(expected_results, list(tree.walkdirs()))
 
775
        finally:
 
776
            tree.unlock()
 
777
 
 
778
    def test_path2id(self):
 
779
        # smoke test for path2id
 
780
        tree = self.make_branch_and_tree('.')
 
781
        self.build_tree(['foo'])
 
782
        tree.add(['foo'], ['foo-id'])
 
783
        self.assertEqual('foo-id', tree.path2id('foo'))
 
784
        # the next assertion is for backwards compatability with WorkingTree3,
 
785
        # though its probably a bad idea, it makes things work. Perhaps
 
786
        # it should raise a deprecation warning?
 
787
        self.assertEqual('foo-id', tree.path2id('foo/'))
 
788
 
 
789
    def test_filter_unversioned_files(self):
 
790
        # smoke test for filter_unversioned_files
 
791
        tree = self.make_branch_and_tree('.')
 
792
        paths = ['here-and-versioned', 'here-and-not-versioned',
 
793
            'not-here-and-versioned', 'not-here-and-not-versioned']
 
794
        tree.add(['here-and-versioned', 'not-here-and-versioned'],
 
795
            kinds=['file', 'file'])
 
796
        self.build_tree(['here-and-versioned', 'here-and-not-versioned'])
 
797
        tree.lock_read()
 
798
        self.addCleanup(tree.unlock)
 
799
        self.assertEqual(
 
800
            set(['not-here-and-not-versioned', 'here-and-not-versioned']),
 
801
            tree.filter_unversioned_files(paths))
 
802
 
 
803
    def test_detect_real_kind(self):
 
804
        # working trees report the real kind of the file on disk, not the kind
 
805
        # they had when they were first added
 
806
        # create one file of every interesting type
 
807
        tree = self.make_branch_and_tree('.')
 
808
        self.build_tree(['file', 'directory/'])
 
809
        names = ['file', 'directory']
 
810
        if has_symlinks():
 
811
            os.symlink('target', 'symlink')
 
812
            names.append('symlink')
 
813
        tree.add(names, [n + '-id' for n in names])
 
814
        if tree.supports_tree_reference():
 
815
            sub_tree = self.make_branch_and_tree('tree-reference')
 
816
            sub_tree.set_root_id('tree-reference-id')
 
817
            sub_tree.commit('message')
 
818
            names.append('tree-reference')
 
819
            tree.add_reference(sub_tree)
 
820
        # now when we first look, we should see everything with the same kind
 
821
        # with which they were initially added
 
822
        for n in names:
 
823
            actual_kind = tree.kind(n + '-id')
 
824
            self.assertEqual(n, actual_kind)
 
825
        # move them around so the names no longer correspond to the types
 
826
        os.rename(names[0], 'tmp')
 
827
        for i in range(1, len(names)):
 
828
            os.rename(names[i], names[i-1])
 
829
        os.rename('tmp', names[-1])
 
830
        # now look and expect to see the correct types again
 
831
        for i in range(len(names)):
 
832
            actual_kind = tree.kind(names[i-1] + '-id')
 
833
            expected_kind = names[i]
 
834
            self.assertEqual(expected_kind, actual_kind)
 
835
 
 
836
    def test_stored_kind_with_missing(self):
 
837
        tree = self.make_branch_and_tree('tree')
 
838
        tree.lock_write()
 
839
        self.addCleanup(tree.unlock)
 
840
        self.build_tree(['tree/a', 'tree/b/'])
 
841
        tree.add(['a', 'b'], ['a-id', 'b-id'])
 
842
        os.unlink('tree/a')
 
843
        os.rmdir('tree/b')
 
844
        self.assertEqual('file', tree.stored_kind('a-id'))
 
845
        self.assertEqual('directory', tree.stored_kind('b-id'))
 
846
 
 
847
    def test_missing_file_sha1(self):
 
848
        """If a file is missing, its sha1 should be reported as None."""
 
849
        tree = self.make_branch_and_tree('.')
 
850
        tree.lock_write()
 
851
        self.addCleanup(tree.unlock)
 
852
        self.build_tree(['file'])
 
853
        tree.add('file', 'file-id')
 
854
        tree.commit('file added')
 
855
        os.unlink('file')
 
856
        self.assertIs(None, tree.get_file_sha1('file-id'))
 
857
 
 
858
    def test_no_file_sha1(self):
 
859
        """If a file is not present, get_file_sha1 should raise NoSuchId"""
 
860
        tree = self.make_branch_and_tree('.')
 
861
        tree.lock_write()
 
862
        self.addCleanup(tree.unlock)
 
863
        self.assertRaises(errors.NoSuchId, tree.get_file_sha1, 'file-id')
 
864
        self.build_tree(['file'])
 
865
        tree.add('file', 'file-id')
 
866
        tree.commit('foo')
 
867
        tree.remove('file')
 
868
        self.assertRaises(errors.NoSuchId, tree.get_file_sha1, 'file-id')
 
869
 
 
870
    def test_case_sensitive(self):
 
871
        """If filesystem is case-sensitive, tree should report this.
 
872
 
 
873
        We check case-sensitivity by creating a file with a lowercase name,
 
874
        then testing whether it exists with an uppercase name.
 
875
        """
 
876
        self.build_tree(['filename'])
 
877
        if os.path.exists('FILENAME'):
 
878
            case_sensitive = False
 
879
        else:
 
880
            case_sensitive = True
 
881
        tree = self.make_branch_and_tree('test')
 
882
        if tree.__class__ == WorkingTree2:
 
883
            raise TestSkipped('WorkingTree2 is not supported')
 
884
        self.assertEqual(case_sensitive, tree.case_sensitive)
 
885
 
 
886
    def test_all_file_ids_with_missing(self):
 
887
        tree = self.make_branch_and_tree('tree')
 
888
        tree.lock_write()
 
889
        self.addCleanup(tree.unlock)
 
890
        self.build_tree(['tree/a', 'tree/b'])
 
891
        tree.add(['a', 'b'], ['a-id', 'b-id'])
 
892
        os.unlink('tree/a')
 
893
        self.assertEqual(set(['a-id', 'b-id', tree.get_root_id()]),
 
894
                         tree.all_file_ids())
 
895
 
 
896
    def test_sprout_hardlink(self):
 
897
        real_os_link = getattr(os, 'link', None)
 
898
        if real_os_link is None:
 
899
            raise TestNotApplicable("This platform doesn't provide os.link")
 
900
        source = self.make_branch_and_tree('source')
 
901
        self.build_tree(['source/file'])
 
902
        source.add('file')
 
903
        source.commit('added file')
 
904
        def fake_link(source, target):
 
905
            raise OSError(errno.EPERM, 'Operation not permitted')
 
906
        os.link = fake_link
 
907
        try:
 
908
            # Hard-link support is optional, so supplying hardlink=True may
 
909
            # or may not raise an exception.  But if it does, it must be
 
910
            # HardLinkNotSupported
 
911
            try:
 
912
                source.bzrdir.sprout('target', accelerator_tree=source,
 
913
                                     hardlink=True)
 
914
            except errors.HardLinkNotSupported:
 
915
                pass
 
916
        finally:
 
917
            os.link = real_os_link
 
918
 
 
919
 
 
920
class TestIllegalPaths(TestCaseWithWorkingTree):
 
921
 
 
922
    def test_bad_fs_path(self):
 
923
        if osutils.normalizes_filenames():
 
924
            # You *can't* create an illegal filename on OSX.
 
925
            raise tests.TestNotApplicable('OSX normalizes filenames')
 
926
        self.requireFeature(tests.UTF8Filesystem)
 
927
        # We require a UTF8 filesystem, because otherwise we would need to get
 
928
        # tricky to figure out how to create an illegal filename.
 
929
        # \xb5 is an illegal path because it should be \xc2\xb5 for UTF-8
 
930
        tree = self.make_branch_and_tree('tree')
 
931
        self.build_tree(['tree/subdir/'])
 
932
        tree.add('subdir')
 
933
 
 
934
        f = open('tree/subdir/m\xb5', 'wb')
 
935
        try:
 
936
            f.write('trivial\n')
 
937
        finally:
 
938
            f.close()
 
939
 
 
940
        tree.lock_read()
 
941
        self.addCleanup(tree.unlock)
 
942
        basis = tree.basis_tree()
 
943
        basis.lock_read()
 
944
        self.addCleanup(basis.unlock)
 
945
 
 
946
        e = self.assertListRaises(errors.BadFilenameEncoding,
 
947
                                  tree.iter_changes, tree.basis_tree(),
 
948
                                                     want_unversioned=True)
 
949
        # We should display the relative path
 
950
        self.assertEqual('subdir/m\xb5', e.filename)
 
951
        self.assertEqual(osutils._fs_enc, e.fs_encoding)