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