/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(".")
2903.2.3 by Martin Pool
CommitBuilder tests should expect the root to be in the delta iff it's changed in the commit
142
        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
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,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
150
                    tree.get_root_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)
2903.2.3 by Martin Pool
CommitBuilder tests should expect the root to be in the delta iff it's changed in the commit
155
            # if the repository format recorded a new root revision, that
156
            # should be in the delta
157
            got_new_revision = ie.revision != old_revision_id
158
            if got_new_revision:
159
                self.assertEqual(
160
                    ('', '', ie.file_id, ie),
161
                    delta)
162
            else:
163
                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
164
            builder.abort()
165
        except:
166
            builder.abort()
167
            tree.unlock()
168
            raise
169
        else:
170
            tree.unlock()
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
171
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
172
    def test_commit(self):
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
173
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
174
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
175
        try:
176
            builder = tree.branch.get_commit_builder([])
177
            self.record_root(builder, tree)
178
            builder.finish_inventory()
179
            rev_id = builder.commit('foo bar')
180
        finally:
181
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
182
        self.assertNotEqual(None, rev_id)
183
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
1757.1.2 by Robert Collins
Bugfix CommitBuilders recording of the inventory revision id.
184
        # the revision id must be set on the inventory when saving it. This does not
185
        # precisely test that - a repository that wants to can add it on deserialisation,
186
        # but thats all the current contract guarantees anyway.
187
        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
188
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
189
    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
190
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
191
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
192
        try:
193
            builder = tree.branch.get_commit_builder([])
194
            self.record_root(builder, tree)
195
            builder.finish_inventory()
196
            rev_id = builder.commit('foo bar')
197
        finally:
198
            tree.unlock()
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
199
        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
200
        # Just a couple simple tests to ensure that it actually follows
201
        # the RevisionTree api.
202
        self.assertEqual(rev_id, rev_tree.get_revision_id())
203
        self.assertEqual([], rev_tree.get_parent_ids())
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
204
205
    def test_root_entry_has_revision(self):
206
        # test the root revision created and put in the basis
207
        # has the right rev id.
208
        tree = self.make_branch_and_tree('.')
209
        rev_id = tree.commit('message')
210
        basis_tree = tree.basis_tree()
211
        basis_tree.lock_read()
212
        self.addCleanup(basis_tree.unlock)
213
        self.assertEqual(rev_id, basis_tree.inventory.root.revision)
214
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
215
    def _get_revtrees(self, tree, revision_ids):
2592.3.214 by Robert Collins
Merge bzr.dev.
216
        tree.lock_read()
217
        try:
218
            trees = list(tree.branch.repository.revision_trees(revision_ids))
219
            for _tree in trees:
220
                _tree.lock_read()
221
                self.addCleanup(_tree.unlock)
222
            return trees
223
        finally:
224
            tree.unlock()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
225
226
    def test_last_modified_revision_after_commit_root_unchanged(self):
227
        # commiting without changing the root does not change the 
228
        # last modified except on non-rich-root-repositories.
229
        tree = self.make_branch_and_tree('.')
230
        rev1 = tree.commit('')
231
        rev2 = tree.commit('')
232
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
233
        self.assertEqual(rev1, tree1.inventory.root.revision)
234
        if tree.branch.repository.supports_rich_root():
235
            self.assertEqual(rev1, tree2.inventory.root.revision)
236
        else:
237
            self.assertEqual(rev2, tree2.inventory.root.revision)
238
239
    def _add_commit_check_unchanged(self, tree, name):
240
        tree.add([name], [name + 'id'])
241
        rev1 = tree.commit('')
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
242
        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.
243
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
244
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
245
        self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
246
        self.assertFileAncestry([rev1], tree, name)
247
248
    def test_last_modified_revision_after_commit_dir_unchanged(self):
249
        # committing without changing a dir does not change the last modified.
250
        tree = self.make_branch_and_tree('.')
251
        self.build_tree(['dir/'])
252
        self._add_commit_check_unchanged(tree, 'dir')
253
254
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
255
        # committing without changing a dir does not change the last modified
256
        # of the dir even the dirs contents are changed.
257
        tree = self.make_branch_and_tree('.')
258
        self.build_tree(['dir/'])
259
        tree.add(['dir'], ['dirid'])
260
        rev1 = tree.commit('')
261
        self.build_tree(['dir/content'])
262
        tree.add(['dir/content'], ['contentid'])
263
        rev2 = tree.commit('')
264
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
265
        self.assertEqual(rev1, tree1.inventory['dirid'].revision)
266
        self.assertEqual(rev1, tree2.inventory['dirid'].revision)
267
        self.assertFileAncestry([rev1], tree, 'dir')
268
269
    def test_last_modified_revision_after_commit_file_unchanged(self):
270
        # committing without changing a file does not change the last modified.
271
        tree = self.make_branch_and_tree('.')
272
        self.build_tree(['file'])
273
        self._add_commit_check_unchanged(tree, 'file')
274
275
    def test_last_modified_revision_after_commit_link_unchanged(self):
276
        # committing without changing a link does not change the last modified.
277
        self.requireFeature(tests.SymlinkFeature)
278
        tree = self.make_branch_and_tree('.')
279
        os.symlink('target', 'link')
280
        self._add_commit_check_unchanged(tree, 'link')
281
282
    def _add_commit_renamed_check_changed(self, tree, name):
283
        def rename():
284
            tree.rename_one(name, 'new_' + name)
285
        self._add_commit_change_check_changed(tree, name, rename)
286
287
    def test_last_modified_revision_after_rename_dir_changes(self):
288
        # renaming a dir changes the last modified.
289
        tree = self.make_branch_and_tree('.')
290
        self.build_tree(['dir/'])
291
        self._add_commit_renamed_check_changed(tree, 'dir')
292
293
    def test_last_modified_revision_after_rename_file_changes(self):
294
        # renaming a file changes the last modified.
295
        tree = self.make_branch_and_tree('.')
296
        self.build_tree(['file'])
297
        self._add_commit_renamed_check_changed(tree, 'file')
298
299
    def test_last_modified_revision_after_rename_link_changes(self):
300
        # renaming a link changes the last modified.
301
        self.requireFeature(tests.SymlinkFeature)
302
        tree = self.make_branch_and_tree('.')
303
        os.symlink('target', 'link')
304
        self._add_commit_renamed_check_changed(tree, 'link')
305
306
    def _add_commit_reparent_check_changed(self, tree, name):
307
        self.build_tree(['newparent/'])
308
        tree.add(['newparent'])
309
        def reparent():
310
            tree.rename_one(name, 'newparent/new_' + name)
311
        self._add_commit_change_check_changed(tree, name, reparent)
312
313
    def test_last_modified_revision_after_reparent_dir_changes(self):
314
        # reparenting a dir changes the last modified.
315
        tree = self.make_branch_and_tree('.')
316
        self.build_tree(['dir/'])
317
        self._add_commit_reparent_check_changed(tree, 'dir')
318
319
    def test_last_modified_revision_after_reparent_file_changes(self):
320
        # reparenting a file changes the last modified.
321
        tree = self.make_branch_and_tree('.')
322
        self.build_tree(['file'])
323
        self._add_commit_reparent_check_changed(tree, 'file')
324
325
    def test_last_modified_revision_after_reparent_link_changes(self):
326
        # reparenting a link changes the last modified.
327
        self.requireFeature(tests.SymlinkFeature)
328
        tree = self.make_branch_and_tree('.')
329
        os.symlink('target', 'link')
330
        self._add_commit_reparent_check_changed(tree, 'link')
331
332
    def _add_commit_change_check_changed(self, tree, name, changer):
333
        tree.add([name], [name + 'id'])
334
        rev1 = tree.commit('')
335
        changer()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
336
        rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'))
337
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
338
        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
339
        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
340
        self.assertFileAncestry([rev1, rev2], tree, name)
341
342
    def mini_commit(self, tree, name, new_name, records_version=True,
343
        delta_against_basis=True):
344
        """Perform a miniature commit looking for record entry results.
345
        
346
        :param tree: The tree to commit.
347
        :param name: The path in the basis tree of the tree being committed.
348
        :param new_name: The path in the tree being committed.
349
        :param records_version: True if the commit of new_name is expected to
350
            record a new version.
351
        :param delta_against_basis: True of the commit of new_name is expected
352
            to have a delta against the basis.
353
        """
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
354
        tree.lock_write()
355
        try:
356
            # mini manual commit here so we can check the return of
357
            # record_entry_contents.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
358
            parent_ids = tree.get_parent_ids()
359
            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
360
            parent_tree = tree.basis_tree()
361
            parent_tree.lock_read()
362
            self.addCleanup(parent_tree.unlock)
363
            parent_invs = [parent_tree.inventory]
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
364
            for parent_id in parent_ids[1:]:
365
                parent_invs.append(tree.branch.repository.revision_tree(
366
                    parent_id).inventory)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
367
            # root
368
            builder.record_entry_contents(
369
                inventory.make_entry('directory', '', None,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
370
                    tree.get_root_id()), parent_invs, '', tree,
2776.4.13 by Robert Collins
Merge bzr.dev.
371
                    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
372
            def commit_id(file_id):
373
                old_ie = tree.inventory[file_id]
374
                path = tree.id2path(file_id)
375
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
376
                    old_ie.parent_id, file_id)
2776.4.13 by Robert Collins
Merge bzr.dev.
377
                return builder.record_entry_contents(ie, parent_invs, path,
378
                    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
379
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
380
            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
381
            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('')
382
            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
383
                commit_id(parent_id)
384
            # because a change of some sort is meant to have occurred,
385
            # recording the entry must return True.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
386
            delta, version_recorded = commit_id(file_id)
387
            if records_version:
388
                self.assertTrue(version_recorded)
389
            else:
390
                self.assertFalse(version_recorded)
391
            new_entry = builder.new_inventory[file_id]
392
            if delta_against_basis:
393
                expected_delta = (name, new_name, file_id, new_entry)
394
            else:
395
                expected_delta = None
396
            if expected_delta != delta:
397
                import pdb;pdb.set_trace()
398
            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
399
            builder.finish_inventory()
400
            rev2 = builder.commit('')
401
            tree.set_parent_ids([rev2])
402
        except:
403
            builder.abort()
404
            tree.unlock()
405
            raise
406
        else:
407
            tree.unlock()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
408
        return rev2
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
409
410
    def assertFileAncestry(self, ancestry, tree, name, alt_ancestry=None):
411
        # all the changes that have occured should be in the ancestry
412
        # (closest to a public per-file graph API we have today)
413
        tree.lock_read()
414
        self.addCleanup(tree.unlock)
415
        vw = tree.branch.repository.weave_store.get_weave(name + 'id',
416
            tree.branch.repository.get_transaction())
417
        result = vw.get_ancestry([ancestry[-1]])
418
        if alt_ancestry is None:
419
            self.assertEqual(ancestry, result)
420
        else:
421
            self.assertSubset([tuple(result)],
422
                [tuple(ancestry), tuple(alt_ancestry)])
423
424
    def test_last_modified_revision_after_content_file_changes(self):
425
        # altering a file changes the last modified.
426
        tree = self.make_branch_and_tree('.')
427
        self.build_tree(['file'])
428
        def change_file():
429
            tree.put_file_bytes_non_atomic('fileid', 'new content')
430
        self._add_commit_change_check_changed(tree, 'file', change_file)
431
432
    def test_last_modified_revision_after_content_link_changes(self):
433
        # changing a link changes the last modified.
434
        self.requireFeature(tests.SymlinkFeature)
435
        tree = self.make_branch_and_tree('.')
436
        os.symlink('target', 'link')
437
        def change_link():
438
            os.unlink('link')
439
            os.symlink('newtarget', 'link')
440
        self._add_commit_change_check_changed(tree, 'link', change_link)
441
442
    def _commit_sprout(self, tree, name):
443
        tree.add([name], [name + 'id'])
444
        rev_id = tree.commit('')
445
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
446
447
    def _rename_in_tree(self, tree, name):
448
        tree.rename_one(name, 'new_' + name)
449
        return tree.commit('')
450
451
    def _commit_sprout_rename_merge(self, tree1, name):
452
        rev1, tree2 = self._commit_sprout(tree1, name)
453
        # change both sides equally
454
        rev2 = self._rename_in_tree(tree1, name)
455
        rev3 = self._rename_in_tree(tree2, name)
456
        tree1.merge_from_branch(tree2.branch)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
457
        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.
458
        tree3, = self._get_revtrees(tree1, [rev4])
459
        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.
460
        # TODO: change this to an assertFileGraph call to check the
461
        # 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.
462
        self.assertFileAncestry([rev1, rev2, rev3, rev4], tree1, name,
463
            [rev1, rev3, rev2, rev4])
464
465
    def test_last_modified_revision_after_merge_dir_changes(self):
466
        # merge a dir changes the last modified.
467
        tree1 = self.make_branch_and_tree('t1')
468
        self.build_tree(['t1/dir/'])
469
        self._commit_sprout_rename_merge(tree1, 'dir')
470
471
    def test_last_modified_revision_after_merge_file_changes(self):
472
        # merge a file changes the last modified.
473
        tree1 = self.make_branch_and_tree('t1')
474
        self.build_tree(['t1/file'])
475
        self._commit_sprout_rename_merge(tree1, 'file')
476
477
    def test_last_modified_revision_after_merge_link_changes(self):
478
        # merge a link changes the last modified.
479
        self.requireFeature(tests.SymlinkFeature)
480
        tree1 = self.make_branch_and_tree('t1')
481
        os.symlink('target', 't1/link')
482
        self._commit_sprout_rename_merge(tree1, 'link')
483
484
    def _commit_sprout_rename_merge_converged(self, tree1, name):
485
        rev1, tree2 = self._commit_sprout(tree1, name)
486
        # change on the other side to merge back
487
        rev2 = self._rename_in_tree(tree2, name)
488
        tree1.merge_from_branch(tree2.branch)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
489
        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.
490
        tree3, = self._get_revtrees(tree1, [rev2])
491
        self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
492
        self.assertFileAncestry([rev1, rev2], tree1, name)
493
494
    def test_last_modified_revision_after_converged_merge_dir_changes(self):
495
        # merge a dir changes the last modified.
496
        tree1 = self.make_branch_and_tree('t1')
497
        self.build_tree(['t1/dir/'])
498
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
499
500
    def test_last_modified_revision_after_converged_merge_file_changes(self):
501
        # merge a file changes the last modified.
502
        tree1 = self.make_branch_and_tree('t1')
503
        self.build_tree(['t1/file'])
504
        self._commit_sprout_rename_merge_converged(tree1, 'file')
505
506
    def test_last_modified_revision_after_converged_merge_link_changes(self):
507
        # merge a link changes the last modified.
508
        self.requireFeature(tests.SymlinkFeature)
509
        tree1 = self.make_branch_and_tree('t1')
510
        os.symlink('target', 't1/link')
511
        self._commit_sprout_rename_merge_converged(tree1, 'link')
512
513
    def make_dir(self, name):
514
        self.build_tree([name + '/'])
515
516
    def make_file(self, name):
517
        self.build_tree([name])
518
519
    def make_link(self, name):
520
        self.requireFeature(tests.SymlinkFeature)
521
        os.symlink('target', name)
522
523
    def _check_kind_change(self, make_before, make_after):
524
        tree = self.make_branch_and_tree('.')
525
        path = 'name'
526
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
527
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
528
        def change_kind():
2831.5.2 by Vincent Ladeuil
Review feedback.
529
            osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
530
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
531
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
532
        self._add_commit_change_check_changed(tree, path, change_kind)
533
534
    def test_last_modified_dir_file(self):
535
        self._check_kind_change(self.make_dir, self.make_file)
536
537
    def test_last_modified_dir_link(self):
538
        self._check_kind_change(self.make_dir, self.make_link)
539
540
    def test_last_modified_link_file(self):
541
        self._check_kind_change(self.make_link, self.make_file)
542
543
    def test_last_modified_link_dir(self):
544
        self._check_kind_change(self.make_link, self.make_dir)
545
546
    def test_last_modified_file_dir(self):
547
        self._check_kind_change(self.make_file, self.make_dir)
548
549
    def test_last_modified_file_link(self):
550
        self._check_kind_change(self.make_file, self.make_link)