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