14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
from cStringIO import StringIO
18
from io import BytesIO
22
from bzrlib.branch import Branch
23
import bzrlib.bzrdir as bzrdir
24
from bzrlib.bzrdir import BzrDir
25
from bzrlib.conflicts import *
26
import bzrlib.errors as errors
27
from bzrlib.errors import NotBranchError, NotVersionedError
28
from bzrlib.lockdir import LockDir
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.tests import TestCaseWithTransport, TestSkipped
31
from bzrlib.trace import mutter
32
from bzrlib.transport import get_transport
33
import bzrlib.workingtree as workingtree
34
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
30
workingtree as bzrworkingtree,
34
from ..lock import write_locked
35
from ..lockdir import LockDir
36
from . import TestCase, TestCaseWithTransport, TestSkipped
44
from .features import SymlinkFeature
37
46
class TestTreeDirectory(TestCaseWithTransport):
61
70
class TestDefaultFormat(TestCaseWithTransport):
63
72
def test_get_set_default_format(self):
64
old_format = workingtree.WorkingTreeFormat.get_default_format()
66
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
67
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
69
# the default branch format is used by the meta dir format
70
# which is not the default bzrdir format at this point
71
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
72
dir.create_repository()
74
result = dir.create_workingtree()
75
self.assertEqual(result, 'A tree')
77
workingtree.WorkingTreeFormat.set_default_format(old_format)
78
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
81
class SampleTreeFormat(workingtree.WorkingTreeFormat):
73
old_format = workingtree.format_registry.get_default()
75
self.assertTrue(isinstance(
76
old_format, workingtree_4.WorkingTreeFormat6))
77
workingtree.format_registry.set_default(SampleTreeFormat())
79
# the default branch format is used by the meta dir format
80
# which is not the default bzrdir format at this point
81
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
82
dir.create_repository()
84
result = dir.create_workingtree()
85
self.assertEqual(result, 'A tree')
87
workingtree.format_registry.set_default(old_format)
88
self.assertEqual(old_format, workingtree.format_registry.get_default())
90
def test_from_string(self):
91
self.assertIsInstance(
92
SampleTreeFormat.from_string(b"Sample tree format."),
95
AssertionError, SampleTreeFormat.from_string,
96
b"Different format string.")
98
def test_get_set_default_format_by_key(self):
99
old_format = workingtree.format_registry.get_default()
101
format = SampleTreeFormat()
102
workingtree.format_registry.register(format)
103
self.addCleanup(workingtree.format_registry.remove, format)
104
self.assertTrue(isinstance(
105
old_format, workingtree_4.WorkingTreeFormat6))
106
workingtree.format_registry.set_default_key(format.get_format_string())
108
# the default branch format is used by the meta dir format
109
# which is not the default bzrdir format at this point
110
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
111
dir.create_repository()
113
result = dir.create_workingtree()
114
self.assertEqual(result, 'A tree')
116
workingtree.format_registry.set_default_key(
117
old_format.get_format_string())
118
self.assertEqual(old_format, workingtree.format_registry.get_default())
121
tree = self.make_branch_and_tree('.')
122
open_direct = workingtree.WorkingTree.open('.')
123
self.assertEqual(tree.basedir, open_direct.basedir)
124
open_no_args = workingtree.WorkingTree.open()
125
self.assertEqual(tree.basedir, open_no_args.basedir)
127
def test_open_containing(self):
128
tree = self.make_branch_and_tree('.')
129
open_direct, relpath = workingtree.WorkingTree.open_containing('.')
130
self.assertEqual(tree.basedir, open_direct.basedir)
131
self.assertEqual('', relpath)
132
open_no_args, relpath = workingtree.WorkingTree.open_containing()
133
self.assertEqual(tree.basedir, open_no_args.basedir)
134
self.assertEqual('', relpath)
135
open_subdir, relpath = workingtree.WorkingTree.open_containing(
137
self.assertEqual(tree.basedir, open_subdir.basedir)
138
self.assertEqual('subdir', relpath)
141
class SampleTreeFormat(bzrworkingtree.WorkingTreeFormatMetaDir):
82
142
"""A sample format
84
this format is initializable, unsupported to aid in testing the
144
this format is initializable, unsupported to aid in testing the
85
145
open and open_downlevel routines.
88
def get_format_string(self):
149
def get_format_string(cls):
89
150
"""See WorkingTreeFormat.get_format_string()."""
90
return "Sample tree format."
151
return b"Sample tree format."
92
def initialize(self, a_bzrdir, revision_id=None):
153
def initialize(self, a_controldir, revision_id=None, from_branch=None,
154
accelerator_tree=None, hardlink=False):
93
155
"""Sample branches cannot be created."""
94
t = a_bzrdir.get_workingtree_transport(self)
95
t.put('format', StringIO(self.get_format_string()))
156
t = a_controldir.get_workingtree_transport(self)
157
t.put_bytes('format', self.get_format_string())
98
160
def is_supported(self):
102
164
return "opened tree."
167
class SampleExtraTreeFormat(workingtree.WorkingTreeFormat):
168
"""A sample format that does not support use in a metadir.
172
def get_format_string(self):
173
# Not usable in a metadir, so no format string
176
def initialize(self, a_controldir, revision_id=None, from_branch=None,
177
accelerator_tree=None, hardlink=False):
178
raise NotImplementedError(self.initialize)
180
def is_supported(self):
183
def open(self, transport, _found=False):
184
raise NotImplementedError(self.open)
105
187
class TestWorkingTreeFormat(TestCaseWithTransport):
106
188
"""Tests for the WorkingTreeFormat facility."""
190
def test_find_format_string(self):
191
# is the right format object found for a working tree?
192
branch = self.make_branch('branch')
194
errors.NoWorkingTree,
195
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string,
197
transport = branch.controldir.get_workingtree_transport(None)
199
transport.put_bytes("format", b"some format name")
200
# The format does not have to be known by Bazaar,
201
# find_format_string just retrieves the name
204
bzrworkingtree.WorkingTreeFormatMetaDir.find_format_string(
108
207
def test_find_format(self):
109
208
# is the right format object found for a working tree?
110
209
# create a branch with a few known format objects.
111
210
self.build_tree(["foo/", "bar/"])
112
212
def check_format(format, url):
113
dir = format._matchingbzrdir.initialize(url)
213
dir = format._matchingcontroldir.initialize(url)
114
214
dir.create_repository()
115
215
dir.create_branch()
116
216
format.initialize(dir)
117
t = get_transport(url)
118
found_format = workingtree.WorkingTreeFormat.find_format(dir)
119
self.failUnless(isinstance(found_format, format.__class__))
120
check_format(workingtree.WorkingTreeFormat3(), "bar")
217
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
219
self.assertIsInstance(found_format, format.__class__)
220
check_format(workingtree_3.WorkingTreeFormat3(), "bar")
122
222
def test_find_format_no_tree(self):
123
223
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
124
224
self.assertRaises(errors.NoWorkingTree,
125
workingtree.WorkingTreeFormat.find_format,
225
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
128
228
def test_find_format_unknown_format(self):
131
231
dir.create_branch()
132
232
SampleTreeFormat().initialize(dir)
133
233
self.assertRaises(errors.UnknownFormatError,
134
workingtree.WorkingTreeFormat.find_format,
234
bzrworkingtree.WorkingTreeFormatMetaDir.find_format,
237
def test_find_format_with_features(self):
238
tree = self.make_branch_and_tree('.', format='2a')
239
tree.update_feature_flags({b"name": b"necessity"})
240
found_format = bzrworkingtree.WorkingTreeFormatMetaDir.find_format(
242
self.assertIsInstance(found_format, workingtree.WorkingTreeFormat)
243
self.assertEqual(found_format.features.get(b"name"), b"necessity")
245
bzrdir.MissingFeature, found_format.check_support_status, True)
247
bzrworkingtree.WorkingTreeFormatMetaDir.unregister_feature,
249
bzrworkingtree.WorkingTreeFormatMetaDir.register_feature(b"name")
250
found_format.check_support_status(True)
253
class TestWorkingTreeIterEntriesByDir_wSubtrees(TestCaseWithTransport):
255
def make_simple_tree(self):
256
tree = self.make_branch_and_tree('tree', format='development-subtree')
257
self.build_tree(['tree/a/', 'tree/a/b/', 'tree/a/b/c'])
258
tree.set_root_id(b'root-id')
259
tree.add(['a', 'a/b', 'a/b/c'], [b'a-id', b'b-id', b'c-id'])
260
tree.commit('initial')
263
def test_just_directory(self):
264
tree = self.make_simple_tree()
265
self.assertEqual([('directory', b'root-id'),
266
('directory', b'a-id'),
267
('directory', b'b-id'),
269
[(ie.kind, ie.file_id)
270
for path, ie in tree.iter_entries_by_dir()])
271
self.make_branch_and_tree('tree/a/b')
272
self.assertEqual([('tree-reference', b'b-id')],
273
[(ie.kind, ie.file_id)
274
for path, ie in tree.iter_entries_by_dir(
275
specific_files=['a/b'])])
277
def test_direct_subtree(self):
278
tree = self.make_simple_tree()
279
self.make_branch_and_tree('tree/a/b')
280
self.assertEqual([('directory', b'root-id'),
281
('directory', b'a-id'),
282
('tree-reference', b'b-id')],
283
[(ie.kind, ie.file_id)
284
for path, ie in tree.iter_entries_by_dir()])
286
def test_indirect_subtree(self):
287
tree = self.make_simple_tree()
288
self.make_branch_and_tree('tree/a')
289
self.assertEqual([('directory', b'root-id'),
290
('tree-reference', b'a-id')],
291
[(ie.kind, ie.file_id)
292
for path, ie in tree.iter_entries_by_dir()])
295
class TestWorkingTreeFormatRegistry(TestCase):
298
super(TestWorkingTreeFormatRegistry, self).setUp()
299
self.registry = workingtree.WorkingTreeFormatRegistry()
137
301
def test_register_unregister_format(self):
138
302
format = SampleTreeFormat()
140
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
141
dir.create_repository()
144
format.initialize(dir)
145
# register a format for it.
146
workingtree.WorkingTreeFormat.register_format(format)
147
# which branch.Open will refuse (not supported)
148
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
149
# but open_downlevel will work
150
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
151
# unregister the format
152
workingtree.WorkingTreeFormat.unregister_format(format)
303
self.registry.register(format)
304
self.assertEqual(format, self.registry.get(b"Sample tree format."))
305
self.registry.remove(format)
306
self.assertRaises(KeyError, self.registry.get, b"Sample tree format.")
308
def test_get_all(self):
309
format = SampleTreeFormat()
310
self.assertEqual([], self.registry._get_all())
311
self.registry.register(format)
312
self.assertEqual([format], self.registry._get_all())
314
def test_register_extra(self):
315
format = SampleExtraTreeFormat()
316
self.assertEqual([], self.registry._get_all())
317
self.registry.register_extra(format)
318
self.assertEqual([format], self.registry._get_all())
320
def test_register_extra_lazy(self):
321
self.assertEqual([], self.registry._get_all())
322
self.registry.register_extra_lazy("breezy.tests.test_workingtree",
323
"SampleExtraTreeFormat")
324
formats = self.registry._get_all()
325
self.assertEqual(1, len(formats))
326
self.assertIsInstance(formats[0], SampleExtraTreeFormat)
155
329
class TestWorkingTreeFormat3(TestCaseWithTransport):
167
341
# stat-cache = ??
168
342
# no inventory.basis yet
169
343
t = control.get_workingtree_transport(None)
170
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
344
self.assertEqualDiff(b'Bazaar-NG Working Tree format 3',
171
345
t.get('format').read())
172
self.assertEqualDiff('<inventory format="5">\n'
174
t.get('inventory').read())
175
self.assertEqualDiff('### bzr hashcache v5\n',
346
self.assertEqualDiff(t.get('inventory').read(),
347
b'<inventory format="5">\n'
350
self.assertEqualDiff(b'### bzr hashcache v5\n',
176
351
t.get('stat-cache').read())
177
352
self.assertFalse(t.has('inventory.basis'))
178
353
# no last-revision file means 'None' or 'NULLREVISION'
179
354
self.assertFalse(t.has('last-revision'))
180
# TODO RBC 20060210 do a commit, check the inventory.basis is created
355
# TODO RBC 20060210 do a commit, check the inventory.basis is created
181
356
# correctly and last-revision file becomes present.
183
358
def test_uses_lockdir(self):
184
359
"""WorkingTreeFormat3 uses its own LockDir:
186
361
- lock is a directory
187
362
- when the WorkingTree is locked, LockDir can see that
189
364
t = self.get_transport()
190
365
url = self.get_url()
191
366
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
192
repo = dir.create_repository()
193
branch = dir.create_branch()
367
dir.create_repository()
195
tree = workingtree.WorkingTreeFormat3().initialize(dir)
370
tree = workingtree_3.WorkingTreeFormat3().initialize(dir)
196
371
except errors.NotLocalUrl:
197
372
raise TestSkipped('Not a local URL')
198
373
self.assertIsDirectory('.bzr', t)
199
374
self.assertIsDirectory('.bzr/checkout', t)
200
375
self.assertIsDirectory('.bzr/checkout/lock', t)
201
376
our_lock = LockDir(t, '.bzr/checkout/lock')
202
self.assertEquals(our_lock.peek(), None)
204
self.assertTrue(our_lock.peek())
206
self.assertEquals(our_lock.peek(), None)
208
def create_format2_tree(self, url):
209
return BzrDir.create_standalone_workingtree(url)
211
def test_conflicts_format2(self):
212
# test backwards compatability
213
tree = self.create_format2_tree('.')
214
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
216
file('lala.BASE', 'wb').write('labase')
217
expected = ContentsConflict('lala')
218
self.assertEqual(list(tree.conflicts()), [expected])
219
file('lala', 'wb').write('la')
220
tree.add('lala', 'lala-id')
221
expected = ContentsConflict('lala', file_id='lala-id')
222
self.assertEqual(list(tree.conflicts()), [expected])
223
file('lala.THIS', 'wb').write('lathis')
224
file('lala.OTHER', 'wb').write('laother')
225
# When "text conflict"s happen, stem, THIS and OTHER are text
226
expected = TextConflict('lala', file_id='lala-id')
227
self.assertEqual(list(tree.conflicts()), [expected])
228
os.unlink('lala.OTHER')
229
os.mkdir('lala.OTHER')
230
expected = ContentsConflict('lala', file_id='lala-id')
231
self.assertEqual(list(tree.conflicts()), [expected])
377
self.assertEqual(our_lock.peek(), None)
378
with tree.lock_write():
379
self.assertTrue(our_lock.peek())
380
self.assertEqual(our_lock.peek(), None)
382
def test_missing_pending_merges(self):
383
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
384
control.create_repository()
385
control.create_branch()
386
tree = workingtree_3.WorkingTreeFormat3().initialize(control)
387
tree._transport.delete("pending-merges")
388
self.assertEqual([], tree.get_parent_ids())
391
class TestRevert(TestCaseWithTransport):
393
def test_revert_conflicts_recursive(self):
394
this_tree = self.make_branch_and_tree('this-tree')
395
self.build_tree_contents([('this-tree/foo/',),
396
('this-tree/foo/bar', b'bar')])
397
this_tree.add(['foo', 'foo/bar'])
398
this_tree.commit('created foo/bar')
399
other_tree = this_tree.controldir.sprout(
400
'other-tree').open_workingtree()
401
self.build_tree_contents([('other-tree/foo/bar', b'baz')])
402
other_tree.commit('changed bar')
403
self.build_tree_contents([('this-tree/foo/bar', b'qux')])
404
this_tree.commit('changed qux')
405
this_tree.merge_from_branch(other_tree.branch)
406
self.assertEqual(1, len(this_tree.conflicts()))
407
this_tree.revert(['foo'])
408
self.assertEqual(0, len(this_tree.conflicts()))
411
class TestAutoResolve(TestCaseWithTransport):
413
def test_auto_resolve(self):
414
base = self.make_branch_and_tree('base')
415
self.build_tree_contents([('base/hello', b'Hello')])
416
base.add('hello', b'hello_id')
418
other = base.controldir.sprout('other').open_workingtree()
419
self.build_tree_contents([('other/hello', b'hELLO')])
420
other.commit('Case switch')
421
this = base.controldir.sprout('this').open_workingtree()
422
self.assertPathExists('this/hello')
423
self.build_tree_contents([('this/hello', b'Hello World')])
424
this.commit('Add World')
425
this.merge_from_branch(other.branch)
426
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
429
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
431
self.build_tree_contents([('this/hello', b'<<<<<<<')])
433
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
435
self.build_tree_contents([('this/hello', b'=======')])
437
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
439
self.build_tree_contents([('this/hello', b'\n>>>>>>>')])
440
remaining, resolved = this.auto_resolve()
441
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
443
self.assertEqual([], resolved)
444
self.build_tree_contents([('this/hello', b'hELLO wORLD')])
445
remaining, resolved = this.auto_resolve()
446
self.assertEqual([], this.conflicts())
447
self.assertEqual([conflicts.TextConflict('hello', b'hello_id')],
449
self.assertPathDoesNotExist('this/hello.BASE')
451
def test_unsupported_symlink_auto_resolve(self):
452
self.requireFeature(SymlinkFeature)
453
base = self.make_branch_and_tree('base')
454
self.build_tree_contents([('base/hello', 'Hello')])
455
base.add('hello', b'hello_id')
456
base.commit('commit 0')
457
other = base.controldir.sprout('other').open_workingtree()
458
self.build_tree_contents([('other/hello', 'Hello')])
459
os.symlink('other/hello', 'other/foo')
460
other.add('foo', b'foo_id')
461
other.commit('commit symlink')
462
this = base.controldir.sprout('this').open_workingtree()
463
self.assertPathExists('this/hello')
464
self.build_tree_contents([('this/hello', 'Hello')])
465
this.commit('commit 2')
467
trace.push_log_file(log)
468
os_symlink = getattr(os, 'symlink', None)
471
this.merge_from_branch(other.branch)
474
os.symlink = os_symlink
475
self.assertContainsRe(
477
b'Unable to create symlink "foo" on this filesystem')
479
def test_auto_resolve_dir(self):
480
tree = self.make_branch_and_tree('tree')
481
self.build_tree(['tree/hello/'])
482
tree.add('hello', b'hello-id')
483
file_conflict = conflicts.TextConflict('file', b'hello-id')
484
tree.set_conflicts(conflicts.ConflictList([file_conflict]))
488
class TestStoredUncommitted(TestCaseWithTransport):
490
def store_uncommitted(self):
491
tree = self.make_branch_and_tree('tree')
492
tree.commit('get root in there')
493
self.build_tree_contents([('tree/file', b'content')])
494
tree.add('file', b'file-id')
495
tree.store_uncommitted()
498
def test_store_uncommitted(self):
499
self.store_uncommitted()
500
self.assertPathDoesNotExist('tree/file')
502
def test_store_uncommitted_no_change(self):
503
tree = self.make_branch_and_tree('tree')
504
tree.commit('get root in there')
505
tree.store_uncommitted()
506
self.assertIs(None, tree.branch.get_unshelver(tree))
508
def test_restore_uncommitted(self):
509
with write_locked(self.store_uncommitted()) as tree:
510
tree.restore_uncommitted()
511
self.assertPathExists('tree/file')
512
self.assertIs(None, tree.branch.get_unshelver(tree))
514
def test_restore_uncommitted_none(self):
515
tree = self.make_branch_and_tree('tree')
516
tree.restore_uncommitted()