80
63
- when the WorkingTree is locked, LockDir can see that
82
65
# this test could be factored into a subclass of tests common to both
83
# format 3 and 4, but for now its not much of an issue as there is only
66
# format 3 and 4, but for now its not much of an issue as there is only one in common.
85
67
t = self.get_transport()
86
68
tree = self.make_workingtree()
87
69
self.assertIsDirectory('.bzr', t)
88
70
self.assertIsDirectory('.bzr/checkout', t)
89
71
self.assertIsDirectory('.bzr/checkout/lock', t)
90
72
our_lock = LockDir(t, '.bzr/checkout/lock')
91
self.assertEqual(our_lock.peek(), None)
73
self.assertEquals(our_lock.peek(), None)
93
75
self.assertTrue(our_lock.peek())
95
self.assertEqual(our_lock.peek(), None)
77
self.assertEquals(our_lock.peek(), None)
97
79
def make_workingtree(self, relpath=''):
98
80
url = self.get_url(relpath)
187
169
tree.branch.pull(subtree.branch)
189
171
# break the repository's legs to make sure it only uses the trees
190
# it's given; any calls to forbidden methods will raise an
172
# it's given; any calls to forbidden methods will raise an
192
174
repo = tree.branch.repository
193
self.overrideAttr(repo, "get_revision", self.fail)
194
self.overrideAttr(repo, "get_inventory", self.fail)
195
self.overrideAttr(repo, "_get_inventory_xml", self.fail)
175
repo.get_revision = self.fail
176
repo.get_inventory = self.fail
177
repo.get_inventory_xml = self.fail
196
178
# try to set the parent trees.
197
179
tree.set_parent_trees([(rev1, rev1_tree)])
199
181
def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
200
"""Getting parent trees from a dirstate tree does not read from the
182
"""Getting parent trees from a dirstate tree does not read from the
201
183
repos inventory store. This is an important part of the dirstate
202
184
performance optimisation work.
212
194
rev1 = subtree.commit('commit in subdir')
213
195
rev1_tree = subtree.basis_tree()
214
196
rev1_tree.lock_read()
215
rev1_tree.root_inventory
216
198
self.addCleanup(rev1_tree.unlock)
217
199
rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
218
200
rev2_tree = subtree.basis_tree()
219
201
rev2_tree.lock_read()
220
rev2_tree.root_inventory
221
203
self.addCleanup(rev2_tree.unlock)
223
205
tree.branch.pull(subtree.branch)
225
207
# break the repository's legs to make sure it only uses the trees
226
# it's given; any calls to forbidden methods will raise an
208
# it's given; any calls to forbidden methods will raise an
228
210
repo = tree.branch.repository
229
# dont uncomment this: the revision object must be accessed to
230
# answer 'get_parent_ids' for the revision tree- dirstate does not
211
# dont uncomment this: the revision object must be accessed to
212
# answer 'get_parent_ids' for the revision tree- dirstate does not
231
213
# cache the parents of a parent tree at this point.
232
214
#repo.get_revision = self.fail
233
self.overrideAttr(repo, "get_inventory", self.fail)
234
self.overrideAttr(repo, "_get_inventory_xml", self.fail)
215
repo.get_inventory = self.fail
216
repo.get_inventory_xml = self.fail
235
217
# set the parent trees.
236
218
tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
237
219
# read the first tree
238
220
result_rev1_tree = tree.revision_tree(rev1)
239
221
# read the second
240
222
result_rev2_tree = tree.revision_tree(rev2)
241
# compare - there should be no differences between the handed and
223
# compare - there should be no differences between the handed and
243
225
self.assertTreesEqual(rev1_tree, result_rev1_tree)
244
226
self.assertTreesEqual(rev2_tree, result_rev2_tree)
246
228
def test_dirstate_doesnt_cache_non_parent_trees(self):
247
"""Getting parent trees from a dirstate tree does not read from the
229
"""Getting parent trees from a dirstate tree does not read from the
248
230
repos inventory store. This is an important part of the dirstate
249
231
performance optimisation work.
274
256
lock_and_call_current_dirstate(tree, 'lock_tree_write')
275
257
self.assertRaises(errors.ObjectNotLocked, tree.current_dirstate)
277
def test_set_parent_trees_uses_update_basis_by_delta(self):
278
builder = self.make_branch_builder('source')
279
builder.start_series()
280
self.addCleanup(builder.finish_series)
281
builder.build_snapshot('A', [], [
282
('add', ('', 'root-id', 'directory', None)),
283
('add', ('a', 'a-id', 'file', 'content\n'))])
284
builder.build_snapshot('B', ['A'], [
285
('modify', ('a-id', 'new content\nfor a\n')),
286
('add', ('b', 'b-id', 'file', 'b-content\n'))])
287
tree = self.make_workingtree('tree')
288
source_branch = builder.get_branch()
289
tree.branch.repository.fetch(source_branch.repository, 'B')
290
tree.pull(source_branch, stop_revision='A')
292
self.addCleanup(tree.unlock)
293
state = tree.current_dirstate()
295
orig_update = state.update_basis_by_delta
296
def log_update_basis_by_delta(delta, new_revid):
297
called.append(new_revid)
298
return orig_update(delta, new_revid)
299
state.update_basis_by_delta = log_update_basis_by_delta
300
basis = tree.basis_tree()
301
self.assertEqual('a-id', basis.path2id('a'))
302
self.assertEqual(None, basis.path2id('b'))
303
def fail_set_parent_trees(trees, ghosts):
304
raise AssertionError('dirstate.set_parent_trees() was called')
305
state.set_parent_trees = fail_set_parent_trees
306
repo = tree.branch.repository
307
tree.pull(source_branch, stop_revision='B')
308
self.assertEqual(['B'], called)
309
basis = tree.basis_tree()
310
self.assertEqual('a-id', basis.path2id('a'))
311
self.assertEqual('b-id', basis.path2id('b'))
313
def test_set_parent_trees_handles_missing_basis(self):
314
builder = self.make_branch_builder('source')
315
builder.start_series()
316
self.addCleanup(builder.finish_series)
317
builder.build_snapshot('A', [], [
318
('add', ('', 'root-id', 'directory', None)),
319
('add', ('a', 'a-id', 'file', 'content\n'))])
320
builder.build_snapshot('B', ['A'], [
321
('modify', ('a-id', 'new content\nfor a\n')),
322
('add', ('b', 'b-id', 'file', 'b-content\n'))])
323
builder.build_snapshot('C', ['A'], [
324
('add', ('c', 'c-id', 'file', 'c-content\n'))])
325
b_c = self.make_branch('branch_with_c')
326
b_c.pull(builder.get_branch(), stop_revision='C')
327
b_b = self.make_branch('branch_with_b')
328
b_b.pull(builder.get_branch(), stop_revision='B')
329
# This is reproducing some of what 'switch' does, just to isolate the
330
# set_parent_trees() step.
331
wt = b_b.create_checkout('tree', lightweight=True)
332
fmt = wt.bzrdir.find_branch_format()
333
fmt.set_reference(wt.bzrdir, None, b_c)
334
# Re-open with the new reference
335
wt = wt.bzrdir.open_workingtree()
336
wt.set_parent_trees([('C', b_c.repository.revision_tree('C'))])
337
self.assertEqual(None, wt.basis_tree().path2id('b'))
339
259
def test_new_dirstate_on_new_lock(self):
340
260
# until we have detection for when a dirstate can be reused, we
341
261
# want to reparse dirstate on every new lock.
552
472
tree = self.make_branch_and_tree('tag', format='dirstate-tags')
553
473
self.assertEqual(inventory.ROOT_ID, tree.get_root_id())
554
474
tree = self.make_branch_and_tree('subtree',
555
format='development-subtree')
475
format='dirstate-with-subtree')
556
476
self.assertNotEqual(inventory.ROOT_ID, tree.get_root_id())
558
478
def test_non_subtree_with_nested_trees(self):
559
479
# prior to dirstate, st/diff/commit ignored nested trees.
560
# dirstate, as opposed to development-subtree, should
480
# dirstate, as opposed to dirstate-with-subtree, should
561
481
# behave the same way.
562
482
tree = self.make_branch_and_tree('.', format='dirstate')
563
483
self.assertFalse(tree.supports_tree_reference())
656
577
self.assertEqual([], changes)
657
578
self.assertEqual(['', 'versioned', 'versioned2'], returned)
659
def test_iter_changes_unversioned_error(self):
660
""" Check if a PathsNotVersionedError is correctly raised and the
661
paths list contains all unversioned entries only.
663
tree = self.make_branch_and_tree('tree')
664
self.build_tree_contents([('tree/bar', '')])
665
tree.add(['bar'], ['bar-id'])
667
self.addCleanup(tree.unlock)
668
tree_iter_changes = lambda files: [
669
c for c in tree.iter_changes(tree.basis_tree(), specific_files=files,
670
require_versioned=True)
672
e = self.assertRaises(errors.PathsNotVersionedError,
673
tree_iter_changes, ['bar', 'foo'])
674
self.assertEqual(e.paths, ['foo'])
676
def test_iter_changes_unversioned_non_ascii(self):
677
"""Unversioned non-ascii paths should be reported as unicode"""
678
self.requireFeature(features.UnicodeFilenameFeature)
679
tree = self.make_branch_and_tree('.')
680
self.build_tree_contents([('f', '')])
681
tree.add(['f'], ['f-id'])
682
def tree_iter_changes(tree, files):
683
return list(tree.iter_changes(tree.basis_tree(),
684
specific_files=files, require_versioned=True))
686
self.addCleanup(tree.unlock)
687
e = self.assertRaises(errors.PathsNotVersionedError,
688
tree_iter_changes, tree, [u'\xa7', u'\u03c0'])
689
self.assertEqual(e.paths, [u'\xa7', u'\u03c0'])
691
def get_tree_with_cachable_file_foo(self):
692
tree = self.make_branch_and_tree('.')
694
self.addCleanup(tree.unlock)
695
self.build_tree_contents([('foo', 'a bit of content for foo\n')])
696
tree.add(['foo'], ['foo-id'])
697
tree.current_dirstate()._cutoff_time = time.time() + 60
700
def test_commit_updates_hash_cache(self):
701
tree = self.get_tree_with_cachable_file_foo()
702
revid = tree.commit('a commit')
703
# tree's dirstate should now have a valid stat entry for foo.
704
entry = tree._get_entry(path='foo')
705
expected_sha1 = osutils.sha_file_by_name('foo')
706
self.assertEqual(expected_sha1, entry[1][0][1])
707
self.assertEqual(len('a bit of content for foo\n'), entry[1][0][2])
709
def test_observed_sha1_cachable(self):
710
tree = self.get_tree_with_cachable_file_foo()
711
expected_sha1 = osutils.sha_file_by_name('foo')
712
statvalue = os.lstat("foo")
713
tree._observed_sha1("foo-id", "foo", (expected_sha1, statvalue))
714
entry = tree._get_entry(path="foo")
715
entry_state = entry[1][0]
716
self.assertEqual(expected_sha1, entry_state[1])
717
self.assertEqual(statvalue.st_size, entry_state[2])
720
tree = tree.bzrdir.open_workingtree()
722
self.addCleanup(tree.unlock)
723
entry = tree._get_entry(path="foo")
724
entry_state = entry[1][0]
725
self.assertEqual(expected_sha1, entry_state[1])
726
self.assertEqual(statvalue.st_size, entry_state[2])
728
def test_observed_sha1_new_file(self):
729
tree = self.make_branch_and_tree('.')
730
self.build_tree(['foo'])
731
tree.add(['foo'], ['foo-id'])
734
current_sha1 = tree._get_entry(path="foo")[1][0][1]
739
tree._observed_sha1("foo-id", "foo",
740
(osutils.sha_file_by_name('foo'), os.lstat("foo")))
741
# Must not have changed
742
self.assertEqual(current_sha1,
743
tree._get_entry(path="foo")[1][0][1])
747
def test_get_file_with_stat_id_only(self):
748
# Explicit test to ensure we get a lstat value from WT4 trees.
749
tree = self.make_branch_and_tree('.')
750
self.build_tree(['foo'])
751
tree.add(['foo'], ['foo-id'])
753
self.addCleanup(tree.unlock)
754
file_obj, statvalue = tree.get_file_with_stat('foo-id')
755
expected = os.lstat('foo')
756
self.assertEqualStat(expected, statvalue)
757
self.assertEqual(["contents of foo\n"], file_obj.readlines())
760
581
class TestCorruptDirstate(TestCaseWithTransport):
761
582
"""Tests for how we handle when the dirstate has been corrupted."""
852
672
('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
853
673
('dir', [(('dir', 'file', 'file-id'), ['a', 'f'])]),
854
674
], self.get_simple_dirblocks(state))
857
class TestInventoryCoherency(TestCaseWithTransport):
859
def test_inventory_is_synced_when_unversioning_a_dir(self):
860
"""Unversioning the root of a subtree unversions the entire subtree."""
861
tree = self.make_branch_and_tree('.')
862
self.build_tree(['a/', 'a/b', 'c/'])
863
tree.add(['a', 'a/b', 'c'], ['a-id', 'b-id', 'c-id'])
864
# within a lock unversion should take effect
866
self.addCleanup(tree.unlock)
867
# Force access to the in memory inventory to trigger bug #494221: try
868
# maintaining the in-memory inventory
869
inv = tree.root_inventory
870
self.assertTrue(inv.has_id('a-id'))
871
self.assertTrue(inv.has_id('b-id'))
872
tree.unversion(['a-id', 'b-id'])
873
self.assertFalse(inv.has_id('a-id'))
874
self.assertFalse(inv.has_id('b-id'))