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