/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1740.3.1 by Jelmer Vernooij
Introduce and use CommitBuilder objects.
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
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
19
import errno
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
20
import os
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
21
import sys
22
23
from bzrlib import (
24
    errors,
25
    inventory,
26
    osutils,
27
    repository,
28
    tests,
29
    )
30
from bzrlib.tests.repository_implementations import test_repository
31
32
33
class TestCommitBuilder(test_repository.TestCaseWithRepository):
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
34
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
35
    def test_get_commit_builder(self):
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
36
        branch = self.make_branch('.')
37
        branch.repository.lock_write()
38
        builder = branch.repository.get_commit_builder(
39
            branch, [], branch.get_config())
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
40
        self.assertIsInstance(builder, repository.CommitBuilder)
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
41
        self.assertTrue(builder.random_revid)
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
42
        branch.repository.commit_write_group()
43
        branch.repository.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
44
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
45
    def record_root(self, builder, tree):
46
        if builder.record_root_entry is True:
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
47
            tree.lock_read()
48
            try:
49
                ie = tree.inventory.root
50
            finally:
51
                tree.unlock()
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
52
            parent_tree = tree.branch.repository.revision_tree(None)
1910.2.22 by Aaron Bentley
Make commits preserve root entry data
53
            parent_invs = []
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
54
            builder.record_entry_contents(ie, parent_invs, '', tree)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
55
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
56
    def test_finish_inventory(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
57
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
58
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
59
        try:
60
            builder = tree.branch.get_commit_builder([])
61
            self.record_root(builder, tree)
62
            builder.finish_inventory()
63
            tree.branch.repository.commit_write_group()
64
        finally:
65
            tree.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
66
2749.3.1 by Jelmer Vernooij
Add CommitBuilder.abort().
67
    def test_abort(self):
68
        tree = self.make_branch_and_tree(".")
69
        tree.lock_write()
70
        try:
71
            builder = tree.branch.get_commit_builder([])
72
            self.record_root(builder, tree)
73
            builder.finish_inventory()
74
            builder.abort()
75
        finally:
76
            tree.unlock()
77
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
78
    def test_commit_message(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
79
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
80
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
81
        try:
82
            builder = tree.branch.get_commit_builder([])
83
            self.record_root(builder, tree)
84
            builder.finish_inventory()
85
            rev_id = builder.commit('foo bar blah')
86
        finally:
87
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
88
        rev = tree.branch.repository.get_revision(rev_id)
89
        self.assertEqual('foo bar blah', rev.message)
90
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
91
    def test_commit_with_revision_id(self):
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
92
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
93
        tree.lock_write()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
94
        try:
2617.6.8 by Robert Collins
Review feedback and documentation.
95
            # use a unicode revision id to test more corner cases.
96
            # The repository layer is meant to handle this.
97
            revision_id = u'\xc8abc'.encode('utf8')
2150.2.2 by Robert Collins
Change the commit builder selected-revision-id test to use a unicode revision id where possible, leading to stricter testing of the hypothetical unicode revision id support in bzr.
98
            try:
2617.6.8 by Robert Collins
Review feedback and documentation.
99
                try:
100
                    builder = tree.branch.get_commit_builder([],
101
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
102
                except errors.NonAsciiRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
103
                    revision_id = 'abc'
104
                    builder = tree.branch.get_commit_builder([],
105
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
106
            except errors.CannotSetRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
107
                # This format doesn't support supplied revision ids
108
                return
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
109
            self.assertFalse(builder.random_revid)
2617.6.8 by Robert Collins
Review feedback and documentation.
110
            self.record_root(builder, tree)
111
            builder.finish_inventory()
112
            self.assertEqual(revision_id, builder.commit('foo bar'))
113
        finally:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
114
            tree.unlock()
2150.2.2 by Robert Collins
Change the commit builder selected-revision-id test to use a unicode revision id where possible, leading to stricter testing of the hypothetical unicode revision id support in bzr.
115
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
116
        # the revision id must be set on the inventory when saving it. This
117
        # does not precisely test that - a repository that wants to can add it
118
        # on deserialisation, but thats all the current contract guarantees
119
        # anyway.
120
        self.assertEqual(revision_id,
121
            tree.branch.repository.get_inventory(revision_id).revision_id)
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
122
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
123
    def test_commit_without_root(self):
124
        """This should cause a deprecation warning, not an assertion failure"""
125
        tree = self.make_branch_and_tree(".")
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
126
        tree.lock_write()
127
        try:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
128
            if tree.branch.repository.supports_rich_root():
129
                raise tests.TestSkipped('Format requires root')
130
            self.build_tree(['foo'])
131
            tree.add('foo', 'foo-id')
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
132
            entry = tree.inventory['foo-id']
133
            builder = tree.branch.get_commit_builder([])
134
            self.callDeprecated(['Root entry should be supplied to'
135
                ' record_entry_contents, as of bzr 0.10.'],
136
                builder.record_entry_contents, entry, [], 'foo', tree)
137
            builder.finish_inventory()
138
            rev_id = builder.commit('foo bar')
139
        finally:
140
            tree.unlock()
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
141
    
142
    def test_commit_unchanged_root(self):
143
        tree = self.make_branch_and_tree(".")
144
        tree.commit('')
145
        tree.lock_write()
146
        parent_tree = tree.basis_tree()
147
        parent_tree.lock_read()
148
        self.addCleanup(parent_tree.unlock)
149
        builder = tree.branch.get_commit_builder([parent_tree.inventory])
150
        try:
151
            ie = inventory.make_entry('directory', '', None,
152
                    tree.inventory.root.file_id)
153
            self.assertFalse(builder.record_entry_contents(
154
                ie, [parent_tree.inventory], '', tree))
155
            builder.abort()
156
        except:
157
            builder.abort()
158
            tree.unlock()
159
            raise
160
        else:
161
            tree.unlock()
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
162
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
163
    def test_commit(self):
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
164
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
165
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
166
        try:
167
            builder = tree.branch.get_commit_builder([])
168
            self.record_root(builder, tree)
169
            builder.finish_inventory()
170
            rev_id = builder.commit('foo bar')
171
        finally:
172
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
173
        self.assertNotEqual(None, rev_id)
174
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
1757.1.2 by Robert Collins
Bugfix CommitBuilders recording of the inventory revision id.
175
        # the revision id must be set on the inventory when saving it. This does not
176
        # precisely test that - a repository that wants to can add it on deserialisation,
177
        # but thats all the current contract guarantees anyway.
178
        self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
179
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
180
    def test_revision_tree(self):
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
181
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
182
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
183
        try:
184
            builder = tree.branch.get_commit_builder([])
185
            self.record_root(builder, tree)
186
            builder.finish_inventory()
187
            rev_id = builder.commit('foo bar')
188
        finally:
189
            tree.unlock()
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
190
        rev_tree = builder.revision_tree()
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
191
        # Just a couple simple tests to ensure that it actually follows
192
        # the RevisionTree api.
193
        self.assertEqual(rev_id, rev_tree.get_revision_id())
194
        self.assertEqual([], rev_tree.get_parent_ids())
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
195
196
    def test_root_entry_has_revision(self):
197
        # test the root revision created and put in the basis
198
        # has the right rev id.
199
        tree = self.make_branch_and_tree('.')
200
        rev_id = tree.commit('message')
201
        basis_tree = tree.basis_tree()
202
        basis_tree.lock_read()
203
        self.addCleanup(basis_tree.unlock)
204
        self.assertEqual(rev_id, basis_tree.inventory.root.revision)
205
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
206
    def _get_revtrees(self, tree, revision_ids):
207
        trees = list(tree.branch.repository.revision_trees(revision_ids))
208
        for tree in trees:
209
            tree.lock_read()
210
            self.addCleanup(tree.unlock)
211
        return trees
212
213
    def test_last_modified_revision_after_commit_root_unchanged(self):
214
        # commiting without changing the root does not change the 
215
        # last modified except on non-rich-root-repositories.
216
        tree = self.make_branch_and_tree('.')
217
        rev1 = tree.commit('')
218
        rev2 = tree.commit('')
219
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
220
        self.assertEqual(rev1, tree1.inventory.root.revision)
221
        if tree.branch.repository.supports_rich_root():
222
            self.assertEqual(rev1, tree2.inventory.root.revision)
223
        else:
224
            self.assertEqual(rev2, tree2.inventory.root.revision)
225
226
    def _add_commit_check_unchanged(self, tree, name):
227
        tree.add([name], [name + 'id'])
228
        rev1 = tree.commit('')
229
        rev2 = tree.commit('')
230
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
231
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
232
        self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
233
        self.assertFileAncestry([rev1], tree, name)
234
235
    def test_last_modified_revision_after_commit_dir_unchanged(self):
236
        # committing without changing a dir does not change the last modified.
237
        tree = self.make_branch_and_tree('.')
238
        self.build_tree(['dir/'])
239
        self._add_commit_check_unchanged(tree, 'dir')
240
241
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
242
        # committing without changing a dir does not change the last modified
243
        # of the dir even the dirs contents are changed.
244
        tree = self.make_branch_and_tree('.')
245
        self.build_tree(['dir/'])
246
        tree.add(['dir'], ['dirid'])
247
        rev1 = tree.commit('')
248
        self.build_tree(['dir/content'])
249
        tree.add(['dir/content'], ['contentid'])
250
        rev2 = tree.commit('')
251
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
252
        self.assertEqual(rev1, tree1.inventory['dirid'].revision)
253
        self.assertEqual(rev1, tree2.inventory['dirid'].revision)
254
        self.assertFileAncestry([rev1], tree, 'dir')
255
256
    def test_last_modified_revision_after_commit_file_unchanged(self):
257
        # committing without changing a file does not change the last modified.
258
        tree = self.make_branch_and_tree('.')
259
        self.build_tree(['file'])
260
        self._add_commit_check_unchanged(tree, 'file')
261
262
    def test_last_modified_revision_after_commit_link_unchanged(self):
263
        # committing without changing a link does not change the last modified.
264
        self.requireFeature(tests.SymlinkFeature)
265
        tree = self.make_branch_and_tree('.')
266
        os.symlink('target', 'link')
267
        self._add_commit_check_unchanged(tree, 'link')
268
269
    def _add_commit_renamed_check_changed(self, tree, name):
270
        def rename():
271
            tree.rename_one(name, 'new_' + name)
272
        self._add_commit_change_check_changed(tree, name, rename)
273
274
    def test_last_modified_revision_after_rename_dir_changes(self):
275
        # renaming a dir changes the last modified.
276
        tree = self.make_branch_and_tree('.')
277
        self.build_tree(['dir/'])
278
        self._add_commit_renamed_check_changed(tree, 'dir')
279
280
    def test_last_modified_revision_after_rename_file_changes(self):
281
        # renaming a file changes the last modified.
282
        tree = self.make_branch_and_tree('.')
283
        self.build_tree(['file'])
284
        self._add_commit_renamed_check_changed(tree, 'file')
285
286
    def test_last_modified_revision_after_rename_link_changes(self):
287
        # renaming a link changes the last modified.
288
        self.requireFeature(tests.SymlinkFeature)
289
        tree = self.make_branch_and_tree('.')
290
        os.symlink('target', 'link')
291
        self._add_commit_renamed_check_changed(tree, 'link')
292
293
    def _add_commit_reparent_check_changed(self, tree, name):
294
        self.build_tree(['newparent/'])
295
        tree.add(['newparent'])
296
        def reparent():
297
            tree.rename_one(name, 'newparent/new_' + name)
298
        self._add_commit_change_check_changed(tree, name, reparent)
299
300
    def test_last_modified_revision_after_reparent_dir_changes(self):
301
        # reparenting a dir changes the last modified.
302
        tree = self.make_branch_and_tree('.')
303
        self.build_tree(['dir/'])
304
        self._add_commit_reparent_check_changed(tree, 'dir')
305
306
    def test_last_modified_revision_after_reparent_file_changes(self):
307
        # reparenting a file changes the last modified.
308
        tree = self.make_branch_and_tree('.')
309
        self.build_tree(['file'])
310
        self._add_commit_reparent_check_changed(tree, 'file')
311
312
    def test_last_modified_revision_after_reparent_link_changes(self):
313
        # reparenting a link changes the last modified.
314
        self.requireFeature(tests.SymlinkFeature)
315
        tree = self.make_branch_and_tree('.')
316
        os.symlink('target', 'link')
317
        self._add_commit_reparent_check_changed(tree, 'link')
318
319
    def _add_commit_change_check_changed(self, tree, name, changer):
320
        tree.add([name], [name + 'id'])
321
        rev1 = tree.commit('')
322
        changer()
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
323
        tree.lock_write()
324
        try:
325
            # mini manual commit here so we can check the return of
326
            # record_entry_contents.
327
            builder = tree.branch.get_commit_builder([tree.last_revision()])
328
            parent_tree = tree.basis_tree()
329
            parent_tree.lock_read()
330
            self.addCleanup(parent_tree.unlock)
331
            parent_invs = [parent_tree.inventory]
332
            # root
333
            builder.record_entry_contents(
334
                inventory.make_entry('directory', '', None,
335
                    tree.inventory.root.file_id), parent_invs, '', tree)
336
            def commit_id(file_id):
337
                old_ie = tree.inventory[file_id]
338
                path = tree.id2path(file_id)
339
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
340
                    old_ie.parent_id, file_id)
341
                return builder.record_entry_contents(ie, parent_invs, path, tree)
342
343
            file_id = name + 'id'
344
            parent_id = tree.inventory[file_id].parent_id
345
            if parent_id != tree.inventory.root.file_id:
346
                commit_id(parent_id)
347
            # because a change of some sort is meant to have occurred,
348
            # recording the entry must return True.
349
            self.assertTrue(commit_id(file_id))
350
            builder.finish_inventory()
351
            rev2 = builder.commit('')
352
            tree.set_parent_ids([rev2])
353
        except:
354
            builder.abort()
355
            tree.unlock()
356
            raise
357
        else:
358
            tree.unlock()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
359
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
360
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
361
        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
362
        self.assertFileAncestry([rev1, rev2], tree, name)
363
364
    def assertFileAncestry(self, ancestry, tree, name, alt_ancestry=None):
365
        # all the changes that have occured should be in the ancestry
366
        # (closest to a public per-file graph API we have today)
367
        tree.lock_read()
368
        self.addCleanup(tree.unlock)
369
        vw = tree.branch.repository.weave_store.get_weave(name + 'id',
370
            tree.branch.repository.get_transaction())
371
        result = vw.get_ancestry([ancestry[-1]])
372
        if alt_ancestry is None:
373
            self.assertEqual(ancestry, result)
374
        else:
375
            self.assertSubset([tuple(result)],
376
                [tuple(ancestry), tuple(alt_ancestry)])
377
378
    def test_last_modified_revision_after_content_file_changes(self):
379
        # altering a file changes the last modified.
380
        tree = self.make_branch_and_tree('.')
381
        self.build_tree(['file'])
382
        def change_file():
383
            tree.put_file_bytes_non_atomic('fileid', 'new content')
384
        self._add_commit_change_check_changed(tree, 'file', change_file)
385
386
    def test_last_modified_revision_after_content_link_changes(self):
387
        # changing a link changes the last modified.
388
        self.requireFeature(tests.SymlinkFeature)
389
        tree = self.make_branch_and_tree('.')
390
        os.symlink('target', 'link')
391
        def change_link():
392
            os.unlink('link')
393
            os.symlink('newtarget', 'link')
394
        self._add_commit_change_check_changed(tree, 'link', change_link)
395
396
    def _commit_sprout(self, tree, name):
397
        tree.add([name], [name + 'id'])
398
        rev_id = tree.commit('')
399
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
400
401
    def _rename_in_tree(self, tree, name):
402
        tree.rename_one(name, 'new_' + name)
403
        return tree.commit('')
404
405
    def _commit_sprout_rename_merge(self, tree1, name):
406
        rev1, tree2 = self._commit_sprout(tree1, name)
407
        # change both sides equally
408
        rev2 = self._rename_in_tree(tree1, name)
409
        rev3 = self._rename_in_tree(tree2, name)
410
        tree1.merge_from_branch(tree2.branch)
411
        rev4 = tree1.commit('')
412
        tree3, = self._get_revtrees(tree1, [rev4])
413
        self.assertEqual(rev4, tree3.inventory[name + 'id'].revision)
414
        self.assertFileAncestry([rev1, rev2, rev3, rev4], tree1, name,
415
            [rev1, rev3, rev2, rev4])
416
417
    def test_last_modified_revision_after_merge_dir_changes(self):
418
        # merge a dir changes the last modified.
419
        tree1 = self.make_branch_and_tree('t1')
420
        self.build_tree(['t1/dir/'])
421
        self._commit_sprout_rename_merge(tree1, 'dir')
422
423
    def test_last_modified_revision_after_merge_file_changes(self):
424
        # merge a file changes the last modified.
425
        tree1 = self.make_branch_and_tree('t1')
426
        self.build_tree(['t1/file'])
427
        self._commit_sprout_rename_merge(tree1, 'file')
428
429
    def test_last_modified_revision_after_merge_link_changes(self):
430
        # merge a link changes the last modified.
431
        self.requireFeature(tests.SymlinkFeature)
432
        tree1 = self.make_branch_and_tree('t1')
433
        os.symlink('target', 't1/link')
434
        self._commit_sprout_rename_merge(tree1, 'link')
435
436
    def _commit_sprout_rename_merge_converged(self, tree1, name):
437
        rev1, tree2 = self._commit_sprout(tree1, name)
438
        # change on the other side to merge back
439
        rev2 = self._rename_in_tree(tree2, name)
440
        tree1.merge_from_branch(tree2.branch)
441
        rev3 = tree1.commit('')
442
        tree3, = self._get_revtrees(tree1, [rev2])
443
        self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
444
        self.assertFileAncestry([rev1, rev2], tree1, name)
445
446
    def test_last_modified_revision_after_converged_merge_dir_changes(self):
447
        # merge a dir changes the last modified.
448
        tree1 = self.make_branch_and_tree('t1')
449
        self.build_tree(['t1/dir/'])
450
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
451
452
    def test_last_modified_revision_after_converged_merge_file_changes(self):
453
        # merge a file changes the last modified.
454
        tree1 = self.make_branch_and_tree('t1')
455
        self.build_tree(['t1/file'])
456
        self._commit_sprout_rename_merge_converged(tree1, 'file')
457
458
    def test_last_modified_revision_after_converged_merge_link_changes(self):
459
        # merge a link changes the last modified.
460
        self.requireFeature(tests.SymlinkFeature)
461
        tree1 = self.make_branch_and_tree('t1')
462
        os.symlink('target', 't1/link')
463
        self._commit_sprout_rename_merge_converged(tree1, 'link')
464
465
    def make_dir(self, name):
466
        self.build_tree([name + '/'])
467
468
    def make_file(self, name):
469
        self.build_tree([name])
470
471
    def make_link(self, name):
472
        self.requireFeature(tests.SymlinkFeature)
473
        os.symlink('target', name)
474
475
    def _check_kind_change(self, make_before, make_after):
476
        tree = self.make_branch_and_tree('.')
477
        path = 'name'
478
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
479
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
480
        def change_kind():
2831.5.2 by Vincent Ladeuil
Review feedback.
481
            osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
482
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
483
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
484
        self._add_commit_change_check_changed(tree, path, change_kind)
485
486
    def test_last_modified_dir_file(self):
487
        self._check_kind_change(self.make_dir, self.make_file)
488
489
    def test_last_modified_dir_link(self):
490
        self._check_kind_change(self.make_dir, self.make_link)
491
492
    def test_last_modified_link_file(self):
493
        self._check_kind_change(self.make_link, self.make_file)
494
495
    def test_last_modified_link_dir(self):
496
        self._check_kind_change(self.make_link, self.make_dir)
497
498
    def test_last_modified_file_dir(self):
499
        self._check_kind_change(self.make_file, self.make_dir)
500
501
    def test_last_modified_file_link(self):
502
        self._check_kind_change(self.make_file, self.make_link)