/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/per_repository/test_commit_builder.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) 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for repository commit builder."""
 
18
 
 
19
from copy import copy
 
20
import errno
 
21
import os
 
22
import sys
 
23
 
 
24
from bzrlib import (
 
25
    errors,
 
26
    inventory,
 
27
    osutils,
 
28
    repository,
 
29
    revision as _mod_revision,
 
30
    tests,
 
31
    )
 
32
from bzrlib.graph import Graph
 
33
from bzrlib.tests.per_repository import test_repository
 
34
 
 
35
 
 
36
class TestCommitBuilder(test_repository.TestCaseWithRepository):
 
37
 
 
38
    def test_get_commit_builder(self):
 
39
        branch = self.make_branch('.')
 
40
        branch.repository.lock_write()
 
41
        builder = branch.repository.get_commit_builder(
 
42
            branch, [], branch.get_config())
 
43
        self.assertIsInstance(builder, repository.CommitBuilder)
 
44
        self.assertTrue(builder.random_revid)
 
45
        branch.repository.commit_write_group()
 
46
        branch.repository.unlock()
 
47
 
 
48
    def record_root(self, builder, tree):
 
49
        if builder.record_root_entry is True:
 
50
            tree.lock_read()
 
51
            try:
 
52
                ie = tree.inventory.root
 
53
            finally:
 
54
                tree.unlock()
 
55
            parent_tree = tree.branch.repository.revision_tree(
 
56
                              _mod_revision.NULL_REVISION)
 
57
            parent_invs = []
 
58
            builder.record_entry_contents(ie, parent_invs, '', tree,
 
59
                tree.path_content_summary(''))
 
60
 
 
61
    def test_finish_inventory(self):
 
62
        tree = self.make_branch_and_tree(".")
 
63
        tree.lock_write()
 
64
        try:
 
65
            builder = tree.branch.get_commit_builder([])
 
66
            self.record_root(builder, tree)
 
67
            builder.finish_inventory()
 
68
            tree.branch.repository.commit_write_group()
 
69
        finally:
 
70
            tree.unlock()
 
71
 
 
72
    def test_abort(self):
 
73
        tree = self.make_branch_and_tree(".")
 
74
        tree.lock_write()
 
75
        try:
 
76
            builder = tree.branch.get_commit_builder([])
 
77
            self.record_root(builder, tree)
 
78
            builder.finish_inventory()
 
79
            builder.abort()
 
80
        finally:
 
81
            tree.unlock()
 
82
 
 
83
    def test_commit_message(self):
 
84
        tree = self.make_branch_and_tree(".")
 
85
        tree.lock_write()
 
86
        try:
 
87
            builder = tree.branch.get_commit_builder([])
 
88
            self.record_root(builder, tree)
 
89
            builder.finish_inventory()
 
90
            rev_id = builder.commit('foo bar blah')
 
91
        finally:
 
92
            tree.unlock()
 
93
        rev = tree.branch.repository.get_revision(rev_id)
 
94
        self.assertEqual('foo bar blah', rev.message)
 
95
 
 
96
    def test_commit_with_revision_id(self):
 
97
        tree = self.make_branch_and_tree(".")
 
98
        tree.lock_write()
 
99
        try:
 
100
            # use a unicode revision id to test more corner cases.
 
101
            # The repository layer is meant to handle this.
 
102
            revision_id = u'\xc8abc'.encode('utf8')
 
103
            try:
 
104
                try:
 
105
                    builder = tree.branch.get_commit_builder([],
 
106
                        revision_id=revision_id)
 
107
                except errors.NonAsciiRevisionId:
 
108
                    revision_id = 'abc'
 
109
                    builder = tree.branch.get_commit_builder([],
 
110
                        revision_id=revision_id)
 
111
            except errors.CannotSetRevisionId:
 
112
                # This format doesn't support supplied revision ids
 
113
                return
 
114
            self.assertFalse(builder.random_revid)
 
115
            self.record_root(builder, tree)
 
116
            builder.finish_inventory()
 
117
            self.assertEqual(revision_id, builder.commit('foo bar'))
 
118
        finally:
 
119
            tree.unlock()
 
120
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
 
121
        # the revision id must be set on the inventory when saving it. This
 
122
        # does not precisely test that - a repository that wants to can add it
 
123
        # on deserialisation, but thats all the current contract guarantees
 
124
        # anyway.
 
125
        self.assertEqual(revision_id,
 
126
            tree.branch.repository.get_inventory(revision_id).revision_id)
 
127
 
 
128
    def test_commit_without_root_errors(self):
 
129
        tree = self.make_branch_and_tree(".")
 
130
        tree.lock_write()
 
131
        try:
 
132
            self.build_tree(['foo'])
 
133
            tree.add('foo', 'foo-id')
 
134
            entry = tree.inventory['foo-id']
 
135
            builder = tree.branch.get_commit_builder([])
 
136
            self.assertRaises(errors.RootMissing,
 
137
                builder.record_entry_contents, entry, [], 'foo', tree,
 
138
                    tree.path_content_summary('foo'))
 
139
            builder.abort()
 
140
        finally:
 
141
            tree.unlock()
 
142
    
 
143
    def test_commit_unchanged_root(self):
 
144
        tree = self.make_branch_and_tree(".")
 
145
        old_revision_id = tree.commit('')
 
146
        tree.lock_write()
 
147
        parent_tree = tree.basis_tree()
 
148
        parent_tree.lock_read()
 
149
        self.addCleanup(parent_tree.unlock)
 
150
        builder = tree.branch.get_commit_builder([parent_tree.inventory])
 
151
        try:
 
152
            ie = inventory.make_entry('directory', '', None,
 
153
                    tree.get_root_id())
 
154
            delta, version_recorded, fs_hash = builder.record_entry_contents(
 
155
                ie, [parent_tree.inventory], '', tree,
 
156
                tree.path_content_summary(''))
 
157
            self.assertFalse(version_recorded)
 
158
            # if the repository format recorded a new root revision, that
 
159
            # should be in the delta
 
160
            got_new_revision = ie.revision != old_revision_id
 
161
            if got_new_revision:
 
162
                self.assertEqual(
 
163
                    ('', '', ie.file_id, ie),
 
164
                    delta)
 
165
            else:
 
166
                self.assertEqual(None, delta)
 
167
            # Directories do not get hashed.
 
168
            self.assertEqual(None, fs_hash)
 
169
            builder.abort()
 
170
        except:
 
171
            builder.abort()
 
172
            tree.unlock()
 
173
            raise
 
174
        else:
 
175
            tree.unlock()
 
176
 
 
177
    def test_commit(self):
 
178
        tree = self.make_branch_and_tree(".")
 
179
        tree.lock_write()
 
180
        try:
 
181
            builder = tree.branch.get_commit_builder([])
 
182
            self.record_root(builder, tree)
 
183
            builder.finish_inventory()
 
184
            rev_id = builder.commit('foo bar')
 
185
        finally:
 
186
            tree.unlock()
 
187
        self.assertNotEqual(None, rev_id)
 
188
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
 
189
        # the revision id must be set on the inventory when saving it. This does not
 
190
        # precisely test that - a repository that wants to can add it on deserialisation,
 
191
        # but thats all the current contract guarantees anyway.
 
192
        self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
 
193
 
 
194
    def test_revision_tree(self):
 
195
        tree = self.make_branch_and_tree(".")
 
196
        tree.lock_write()
 
197
        try:
 
198
            builder = tree.branch.get_commit_builder([])
 
199
            self.record_root(builder, tree)
 
200
            builder.finish_inventory()
 
201
            rev_id = builder.commit('foo bar')
 
202
        finally:
 
203
            tree.unlock()
 
204
        rev_tree = builder.revision_tree()
 
205
        # Just a couple simple tests to ensure that it actually follows
 
206
        # the RevisionTree api.
 
207
        self.assertEqual(rev_id, rev_tree.get_revision_id())
 
208
        self.assertEqual([], rev_tree.get_parent_ids())
 
209
 
 
210
    def test_root_entry_has_revision(self):
 
211
        # test the root revision created and put in the basis
 
212
        # has the right rev id.
 
213
        tree = self.make_branch_and_tree('.')
 
214
        rev_id = tree.commit('message')
 
215
        basis_tree = tree.basis_tree()
 
216
        basis_tree.lock_read()
 
217
        self.addCleanup(basis_tree.unlock)
 
218
        self.assertEqual(rev_id, basis_tree.inventory.root.revision)
 
219
 
 
220
    def _get_revtrees(self, tree, revision_ids):
 
221
        tree.lock_read()
 
222
        try:
 
223
            trees = list(tree.branch.repository.revision_trees(revision_ids))
 
224
            for _tree in trees:
 
225
                _tree.lock_read()
 
226
                self.addCleanup(_tree.unlock)
 
227
            return trees
 
228
        finally:
 
229
            tree.unlock()
 
230
 
 
231
    def test_last_modified_revision_after_commit_root_unchanged(self):
 
232
        # commiting without changing the root does not change the 
 
233
        # last modified except on non-rich-root-repositories.
 
234
        tree = self.make_branch_and_tree('.')
 
235
        rev1 = tree.commit('')
 
236
        rev2 = tree.commit('')
 
237
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
 
238
        self.assertEqual(rev1, tree1.inventory.root.revision)
 
239
        if tree.branch.repository.supports_rich_root():
 
240
            self.assertEqual(rev1, tree2.inventory.root.revision)
 
241
        else:
 
242
            self.assertEqual(rev2, tree2.inventory.root.revision)
 
243
 
 
244
    def _add_commit_check_unchanged(self, tree, name):
 
245
        tree.add([name], [name + 'id'])
 
246
        rev1 = tree.commit('')
 
247
        rev2 = self.mini_commit(tree, name, name, False, False)
 
248
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
 
249
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
 
250
        self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
 
251
        file_id = name + 'id'
 
252
        expected_graph = {}
 
253
        expected_graph[(file_id, rev1)] = ()
 
254
        self.assertFileGraph(expected_graph, tree, (file_id, rev1))
 
255
 
 
256
    def test_last_modified_revision_after_commit_dir_unchanged(self):
 
257
        # committing without changing a dir does not change the last modified.
 
258
        tree = self.make_branch_and_tree('.')
 
259
        self.build_tree(['dir/'])
 
260
        self._add_commit_check_unchanged(tree, 'dir')
 
261
 
 
262
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
 
263
        # committing without changing a dir does not change the last modified
 
264
        # of the dir even the dirs contents are changed.
 
265
        tree = self.make_branch_and_tree('.')
 
266
        self.build_tree(['dir/'])
 
267
        tree.add(['dir'], ['dirid'])
 
268
        rev1 = tree.commit('')
 
269
        self.build_tree(['dir/content'])
 
270
        tree.add(['dir/content'], ['contentid'])
 
271
        rev2 = tree.commit('')
 
272
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
 
273
        self.assertEqual(rev1, tree1.inventory['dirid'].revision)
 
274
        self.assertEqual(rev1, tree2.inventory['dirid'].revision)
 
275
        file_id = 'dirid'
 
276
        expected_graph = {}
 
277
        expected_graph[(file_id, rev1)] = ()
 
278
        self.assertFileGraph(expected_graph, tree, (file_id, rev1))
 
279
 
 
280
    def test_last_modified_revision_after_commit_file_unchanged(self):
 
281
        # committing without changing a file does not change the last modified.
 
282
        tree = self.make_branch_and_tree('.')
 
283
        self.build_tree(['file'])
 
284
        self._add_commit_check_unchanged(tree, 'file')
 
285
 
 
286
    def test_last_modified_revision_after_commit_link_unchanged(self):
 
287
        # committing without changing a link does not change the last modified.
 
288
        self.requireFeature(tests.SymlinkFeature)
 
289
        tree = self.make_branch_and_tree('.')
 
290
        os.symlink('target', 'link')
 
291
        self._add_commit_check_unchanged(tree, 'link')
 
292
 
 
293
    def _add_commit_renamed_check_changed(self, tree, name,
 
294
        expect_fs_hash=False):
 
295
        def rename():
 
296
            tree.rename_one(name, 'new_' + name)
 
297
        self._add_commit_change_check_changed(tree, name, rename,
 
298
            expect_fs_hash=expect_fs_hash)
 
299
 
 
300
    def test_last_modified_revision_after_rename_dir_changes(self):
 
301
        # renaming a dir changes the last modified.
 
302
        tree = self.make_branch_and_tree('.')
 
303
        self.build_tree(['dir/'])
 
304
        self._add_commit_renamed_check_changed(tree, 'dir')
 
305
 
 
306
    def test_last_modified_revision_after_rename_file_changes(self):
 
307
        # renaming a file changes the last modified.
 
308
        tree = self.make_branch_and_tree('.')
 
309
        self.build_tree(['file'])
 
310
        self._add_commit_renamed_check_changed(tree, 'file',
 
311
            expect_fs_hash=True)
 
312
 
 
313
    def test_last_modified_revision_after_rename_link_changes(self):
 
314
        # renaming a link changes the last modified.
 
315
        self.requireFeature(tests.SymlinkFeature)
 
316
        tree = self.make_branch_and_tree('.')
 
317
        os.symlink('target', 'link')
 
318
        self._add_commit_renamed_check_changed(tree, 'link')
 
319
 
 
320
    def _add_commit_reparent_check_changed(self, tree, name,
 
321
        expect_fs_hash=False):
 
322
        self.build_tree(['newparent/'])
 
323
        tree.add(['newparent'])
 
324
        def reparent():
 
325
            tree.rename_one(name, 'newparent/new_' + name)
 
326
        self._add_commit_change_check_changed(tree, name, reparent,
 
327
            expect_fs_hash=expect_fs_hash)
 
328
 
 
329
    def test_last_modified_revision_after_reparent_dir_changes(self):
 
330
        # reparenting a dir changes the last modified.
 
331
        tree = self.make_branch_and_tree('.')
 
332
        self.build_tree(['dir/'])
 
333
        self._add_commit_reparent_check_changed(tree, 'dir')
 
334
 
 
335
    def test_last_modified_revision_after_reparent_file_changes(self):
 
336
        # reparenting a file changes the last modified.
 
337
        tree = self.make_branch_and_tree('.')
 
338
        self.build_tree(['file'])
 
339
        self._add_commit_reparent_check_changed(tree, 'file',
 
340
            expect_fs_hash=True)
 
341
 
 
342
    def test_last_modified_revision_after_reparent_link_changes(self):
 
343
        # reparenting a link changes the last modified.
 
344
        self.requireFeature(tests.SymlinkFeature)
 
345
        tree = self.make_branch_and_tree('.')
 
346
        os.symlink('target', 'link')
 
347
        self._add_commit_reparent_check_changed(tree, 'link')
 
348
 
 
349
    def _add_commit_change_check_changed(self, tree, name, changer,
 
350
        expect_fs_hash=False):
 
351
        tree.add([name], [name + 'id'])
 
352
        rev1 = tree.commit('')
 
353
        changer()
 
354
        rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'),
 
355
            expect_fs_hash=expect_fs_hash)
 
356
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
 
357
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
 
358
        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
 
359
        file_id = name + 'id'
 
360
        expected_graph = {}
 
361
        expected_graph[(file_id, rev1)] = ()
 
362
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
 
363
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
 
364
 
 
365
    def mini_commit(self, tree, name, new_name, records_version=True,
 
366
        delta_against_basis=True, expect_fs_hash=False):
 
367
        """Perform a miniature commit looking for record entry results.
 
368
        
 
369
        :param tree: The tree to commit.
 
370
        :param name: The path in the basis tree of the tree being committed.
 
371
        :param new_name: The path in the tree being committed.
 
372
        :param records_version: True if the commit of new_name is expected to
 
373
            record a new version.
 
374
        :param delta_against_basis: True of the commit of new_name is expected
 
375
            to have a delta against the basis.
 
376
        :param expect_fs_hash: True or false to indicate whether we expect a
 
377
            file hash to be returned from the record_entry_contents call.
 
378
        """
 
379
        tree.lock_write()
 
380
        try:
 
381
            # mini manual commit here so we can check the return of
 
382
            # record_entry_contents.
 
383
            parent_ids = tree.get_parent_ids()
 
384
            builder = tree.branch.get_commit_builder(parent_ids)
 
385
            parent_tree = tree.basis_tree()
 
386
            parent_tree.lock_read()
 
387
            self.addCleanup(parent_tree.unlock)
 
388
            parent_invs = [parent_tree.inventory]
 
389
            for parent_id in parent_ids[1:]:
 
390
                parent_invs.append(tree.branch.repository.revision_tree(
 
391
                    parent_id).inventory)
 
392
            # root
 
393
            builder.record_entry_contents(
 
394
                inventory.make_entry('directory', '', None,
 
395
                    tree.get_root_id()), parent_invs, '', tree,
 
396
                    tree.path_content_summary(''))
 
397
            def commit_id(file_id):
 
398
                old_ie = tree.inventory[file_id]
 
399
                path = tree.id2path(file_id)
 
400
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
 
401
                    old_ie.parent_id, file_id)
 
402
                return builder.record_entry_contents(ie, parent_invs, path,
 
403
                    tree, tree.path_content_summary(path))
 
404
 
 
405
            file_id = tree.path2id(new_name)
 
406
            parent_id = tree.inventory[file_id].parent_id
 
407
            if parent_id != tree.get_root_id():
 
408
                commit_id(parent_id)
 
409
            # because a change of some sort is meant to have occurred,
 
410
            # recording the entry must return True.
 
411
            delta, version_recorded, fs_hash = commit_id(file_id)
 
412
            if records_version:
 
413
                self.assertTrue(version_recorded)
 
414
            else:
 
415
                self.assertFalse(version_recorded)
 
416
            if expect_fs_hash:
 
417
                tree_file_stat = tree.get_file_with_stat(file_id)
 
418
                tree_file_stat[0].close()
 
419
                self.assertEqual(2, len(fs_hash))
 
420
                self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
 
421
                self.assertEqualStat(tree_file_stat[1], fs_hash[1])
 
422
            else:
 
423
                self.assertEqual(None, fs_hash)
 
424
            new_entry = builder.new_inventory[file_id]
 
425
            if delta_against_basis:
 
426
                expected_delta = (name, new_name, file_id, new_entry)
 
427
            else:
 
428
                expected_delta = None
 
429
            self.assertEqual(expected_delta, delta)
 
430
            builder.finish_inventory()
 
431
            rev2 = builder.commit('')
 
432
            tree.set_parent_ids([rev2])
 
433
        except:
 
434
            builder.abort()
 
435
            tree.unlock()
 
436
            raise
 
437
        else:
 
438
            tree.unlock()
 
439
        return rev2
 
440
 
 
441
    def assertFileGraph(self, expected_graph, tree, tip):
 
442
        # all the changes that have occured should be in the ancestry
 
443
        # (closest to a public per-file graph API we have today)
 
444
        tree.lock_read()
 
445
        self.addCleanup(tree.unlock)
 
446
        graph = dict(Graph(tree.branch.repository.texts).iter_ancestry([tip]))
 
447
        self.assertEqual(expected_graph, graph)
 
448
 
 
449
    def test_last_modified_revision_after_content_file_changes(self):
 
450
        # altering a file changes the last modified.
 
451
        tree = self.make_branch_and_tree('.')
 
452
        self.build_tree(['file'])
 
453
        def change_file():
 
454
            tree.put_file_bytes_non_atomic('fileid', 'new content')
 
455
        self._add_commit_change_check_changed(tree, 'file', change_file,
 
456
            expect_fs_hash=True)
 
457
 
 
458
    def test_last_modified_revision_after_content_link_changes(self):
 
459
        # changing a link changes the last modified.
 
460
        self.requireFeature(tests.SymlinkFeature)
 
461
        tree = self.make_branch_and_tree('.')
 
462
        os.symlink('target', 'link')
 
463
        def change_link():
 
464
            os.unlink('link')
 
465
            os.symlink('newtarget', 'link')
 
466
        self._add_commit_change_check_changed(tree, 'link', change_link)
 
467
 
 
468
    def _commit_sprout(self, tree, name):
 
469
        tree.add([name], [name + 'id'])
 
470
        rev_id = tree.commit('')
 
471
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
 
472
 
 
473
    def _rename_in_tree(self, tree, name):
 
474
        tree.rename_one(name, 'new_' + name)
 
475
        return tree.commit('')
 
476
 
 
477
    def _commit_sprout_rename_merge(self, tree1, name, expect_fs_hash=False):
 
478
        rev1, tree2 = self._commit_sprout(tree1, name)
 
479
        # change both sides equally
 
480
        rev2 = self._rename_in_tree(tree1, name)
 
481
        rev3 = self._rename_in_tree(tree2, name)
 
482
        tree1.merge_from_branch(tree2.branch)
 
483
        rev4 = self.mini_commit(tree1, 'new_' + name, 'new_' + name,
 
484
            expect_fs_hash=expect_fs_hash)
 
485
        tree3, = self._get_revtrees(tree1, [rev4])
 
486
        self.assertEqual(rev4, tree3.inventory[name + 'id'].revision)
 
487
        file_id = name + 'id'
 
488
        expected_graph = {}
 
489
        expected_graph[(file_id, rev1)] = ()
 
490
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
 
491
        expected_graph[(file_id, rev3)] = ((file_id, rev1),)
 
492
        expected_graph[(file_id, rev4)] = ((file_id, rev2), (file_id, rev3),)
 
493
        self.assertFileGraph(expected_graph, tree1, (file_id, rev4))
 
494
 
 
495
    def test_last_modified_revision_after_merge_dir_changes(self):
 
496
        # merge a dir changes the last modified.
 
497
        tree1 = self.make_branch_and_tree('t1')
 
498
        self.build_tree(['t1/dir/'])
 
499
        self._commit_sprout_rename_merge(tree1, 'dir')
 
500
 
 
501
    def test_last_modified_revision_after_merge_file_changes(self):
 
502
        # merge a file changes the last modified.
 
503
        tree1 = self.make_branch_and_tree('t1')
 
504
        self.build_tree(['t1/file'])
 
505
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
 
506
 
 
507
    def test_last_modified_revision_after_merge_link_changes(self):
 
508
        # merge a link changes the last modified.
 
509
        self.requireFeature(tests.SymlinkFeature)
 
510
        tree1 = self.make_branch_and_tree('t1')
 
511
        os.symlink('target', 't1/link')
 
512
        self._commit_sprout_rename_merge(tree1, 'link')
 
513
 
 
514
    def _commit_sprout_rename_merge_converged(self, tree1, name):
 
515
        rev1, tree2 = self._commit_sprout(tree1, name)
 
516
        # change on the other side to merge back
 
517
        rev2 = self._rename_in_tree(tree2, name)
 
518
        tree1.merge_from_branch(tree2.branch)
 
519
        rev3 = self.mini_commit(tree1, name, 'new_' + name, False)
 
520
        tree3, = self._get_revtrees(tree1, [rev2])
 
521
        self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
 
522
        file_id = name + 'id'
 
523
        expected_graph = {}
 
524
        expected_graph[(file_id, rev1)] = ()
 
525
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
 
526
        self.assertFileGraph(expected_graph, tree1, (file_id, rev2))
 
527
 
 
528
    def test_last_modified_revision_after_converged_merge_dir_changes(self):
 
529
        # merge a dir changes the last modified.
 
530
        tree1 = self.make_branch_and_tree('t1')
 
531
        self.build_tree(['t1/dir/'])
 
532
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
 
533
 
 
534
    def test_last_modified_revision_after_converged_merge_file_changes(self):
 
535
        # merge a file changes the last modified.
 
536
        tree1 = self.make_branch_and_tree('t1')
 
537
        self.build_tree(['t1/file'])
 
538
        self._commit_sprout_rename_merge_converged(tree1, 'file')
 
539
 
 
540
    def test_last_modified_revision_after_converged_merge_link_changes(self):
 
541
        # merge a link changes the last modified.
 
542
        self.requireFeature(tests.SymlinkFeature)
 
543
        tree1 = self.make_branch_and_tree('t1')
 
544
        os.symlink('target', 't1/link')
 
545
        self._commit_sprout_rename_merge_converged(tree1, 'link')
 
546
 
 
547
    def make_dir(self, name):
 
548
        self.build_tree([name + '/'])
 
549
 
 
550
    def make_file(self, name):
 
551
        self.build_tree([name])
 
552
 
 
553
    def make_link(self, name):
 
554
        self.requireFeature(tests.SymlinkFeature)
 
555
        os.symlink('target', name)
 
556
 
 
557
    def _check_kind_change(self, make_before, make_after, expect_fs_hash=False):
 
558
        tree = self.make_branch_and_tree('.')
 
559
        path = 'name'
 
560
        make_before(path)
 
561
 
 
562
        def change_kind():
 
563
            osutils.delete_any(path)
 
564
            make_after(path)
 
565
 
 
566
        self._add_commit_change_check_changed(tree, path, change_kind,
 
567
            expect_fs_hash=expect_fs_hash)
 
568
 
 
569
    def test_last_modified_dir_file(self):
 
570
        self._check_kind_change(self.make_dir, self.make_file,
 
571
            expect_fs_hash=True)
 
572
 
 
573
    def test_last_modified_dir_link(self):
 
574
        self._check_kind_change(self.make_dir, self.make_link)
 
575
 
 
576
    def test_last_modified_link_file(self):
 
577
        self._check_kind_change(self.make_link, self.make_file,
 
578
            expect_fs_hash=True)
 
579
 
 
580
    def test_last_modified_link_dir(self):
 
581
        self._check_kind_change(self.make_link, self.make_dir)
 
582
 
 
583
    def test_last_modified_file_dir(self):
 
584
        self._check_kind_change(self.make_file, self.make_dir)
 
585
 
 
586
    def test_last_modified_file_link(self):
 
587
        self._check_kind_change(self.make_file, self.make_link)