/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2006-2011 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1740.3.1 by Jelmer Vernooij
Introduce and use CommitBuilder objects.
16
17
"""Tests for repository commit builder."""
18
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
19
import os
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
20
21
from bzrlib import (
5609.31.2 by mbp at sourcefrog
Also turn off whoami inference in per_repository tests
22
    config,
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
23
    errors,
5010.2.24 by Vincent Ladeuil
Fix imports in per_repository/test_commit_builder.py.
24
    graph,
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
25
    inventory,
26
    osutils,
27
    repository,
3668.5.1 by Jelmer Vernooij
Use NULL_REVISION rather than None for Repository.revision_tree().
28
    revision as _mod_revision,
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
29
    tests,
30
    )
5010.2.24 by Vincent Ladeuil
Fix imports in per_repository/test_commit_builder.py.
31
from bzrlib.tests import per_repository
32
33
34
class TestCommitBuilder(per_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()
3668.5.1 by Jelmer Vernooij
Use NULL_REVISION rather than None for Repository.revision_tree().
53
            parent_tree = tree.branch.repository.revision_tree(
54
                              _mod_revision.NULL_REVISION)
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
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
59
    def test_finish_inventory_with_record_root(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([])
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
64
            if not builder.supports_record_entry_contents:
65
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
66
                    "record_entry_contents")
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
67
            repo = tree.branch.repository
2617.6.8 by Robert Collins
Review feedback and documentation.
68
            self.record_root(builder, tree)
69
            builder.finish_inventory()
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
70
            repo.commit_write_group()
71
        finally:
72
            tree.unlock()
73
74
    def test_finish_inventory_record_iter_changes(self):
75
        tree = self.make_branch_and_tree(".")
76
        tree.lock_write()
77
        try:
78
            builder = tree.branch.get_commit_builder([])
79
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
80
                list(builder.record_iter_changes(tree, tree.last_revision(),
81
                    tree.iter_changes(tree.basis_tree())))
3775.2.4 by Robert Collins
Start on a CommitBuilder.record_iter_changes method.
82
                builder.finish_inventory()
83
            except:
84
                builder.abort()
85
                raise
86
            repo = tree.branch.repository
87
            repo.commit_write_group()
2617.6.8 by Robert Collins
Review feedback and documentation.
88
        finally:
89
            tree.unlock()
1740.3.3 by Jelmer Vernooij
Move storing directories and links to commit builder.
90
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
91
    def test_abort_record_entry_contents(self):
2749.3.1 by Jelmer Vernooij
Add CommitBuilder.abort().
92
        tree = self.make_branch_and_tree(".")
93
        tree.lock_write()
94
        try:
95
            builder = tree.branch.get_commit_builder([])
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
96
            if not builder.supports_record_entry_contents:
97
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
98
                    "record_entry_contents")
2749.3.1 by Jelmer Vernooij
Add CommitBuilder.abort().
99
            self.record_root(builder, tree)
100
            builder.finish_inventory()
101
            builder.abort()
102
        finally:
103
            tree.unlock()
104
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
105
    def test_abort_record_iter_changes(self):
106
        tree = self.make_branch_and_tree(".")
107
        tree.lock_write()
108
        try:
109
            builder = tree.branch.get_commit_builder([])
110
            try:
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
111
                basis = tree.basis_tree()
112
                last_rev = tree.last_revision()
113
                changes = tree.iter_changes(basis)
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
114
                list(builder.record_iter_changes(tree, last_rev, changes))
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
115
                builder.finish_inventory()
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
116
            finally:
3775.2.5 by Robert Collins
CommitBuilder.abort() is callable after record_iter_changes.
117
                builder.abort()
118
        finally:
119
            tree.unlock()
120
5777.6.8 by Jelmer Vernooij
Add test for get_commit_builder(lossy=True).
121
    def test_commit_lossy(self):
122
        tree = self.make_branch_and_tree(".")
123
        tree.lock_write()
124
        try:
125
            builder = tree.branch.get_commit_builder([], lossy=True)
126
            list(builder.record_iter_changes(tree, tree.last_revision(),
127
                tree.iter_changes(tree.basis_tree())))
128
            builder.finish_inventory()
129
            rev_id = builder.commit('foo bar blah')
130
        finally:
131
            tree.unlock()
132
        rev = tree.branch.repository.get_revision(rev_id)
133
        self.assertEqual('foo bar blah', rev.message)
134
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
135
    def test_commit_message(self):
1740.3.7 by Jelmer Vernooij
Move committer, log, revprops, timestamp and timezone to CommitBuilder.
136
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
137
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
138
        try:
139
            builder = tree.branch.get_commit_builder([])
5718.4.6 by Jelmer Vernooij
inline function only used once.
140
            list(builder.record_iter_changes(tree, tree.last_revision(),
141
                tree.iter_changes(tree.basis_tree())))
142
            builder.finish_inventory()
2617.6.8 by Robert Collins
Review feedback and documentation.
143
            rev_id = builder.commit('foo bar blah')
144
        finally:
145
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
146
        rev = tree.branch.repository.get_revision(rev_id)
147
        self.assertEqual('foo bar blah', rev.message)
148
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
149
    def test_commit_with_revision_id_record_entry_contents(self):
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
150
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
151
        tree.lock_write()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
152
        try:
2617.6.8 by Robert Collins
Review feedback and documentation.
153
            # use a unicode revision id to test more corner cases.
154
            # The repository layer is meant to handle this.
155
            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.
156
            try:
2617.6.8 by Robert Collins
Review feedback and documentation.
157
                try:
158
                    builder = tree.branch.get_commit_builder([],
159
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
160
                except errors.NonAsciiRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
161
                    revision_id = 'abc'
162
                    builder = tree.branch.get_commit_builder([],
163
                        revision_id=revision_id)
2831.5.1 by Vincent Ladeuil
Portability fix in TestCommitBuilder for unlink.
164
            except errors.CannotSetRevisionId:
2617.6.8 by Robert Collins
Review feedback and documentation.
165
                # This format doesn't support supplied revision ids
166
                return
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
167
            if not builder.supports_record_entry_contents:
168
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
169
                    "record_entry_contents")
2805.6.1 by Robert Collins
Set random_revid on CommitBuilder when a commit generated its own revision id.
170
            self.assertFalse(builder.random_revid)
2617.6.8 by Robert Collins
Review feedback and documentation.
171
            self.record_root(builder, tree)
172
            builder.finish_inventory()
173
            self.assertEqual(revision_id, builder.commit('foo bar'))
174
        finally:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
175
            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.
176
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
177
        # the revision id must be set on the inventory when saving it. This
178
        # does not precisely test that - a repository that wants to can add it
179
        # on deserialisation, but thats all the current contract guarantees
180
        # anyway.
181
        self.assertEqual(revision_id,
182
            tree.branch.repository.get_inventory(revision_id).revision_id)
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
183
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
184
    def test_commit_with_revision_id_record_iter_changes(self):
185
        tree = self.make_branch_and_tree(".")
186
        tree.lock_write()
187
        try:
188
            # use a unicode revision id to test more corner cases.
189
            # The repository layer is meant to handle this.
190
            revision_id = u'\xc8abc'.encode('utf8')
191
            try:
192
                try:
193
                    builder = tree.branch.get_commit_builder([],
194
                        revision_id=revision_id)
195
                except errors.NonAsciiRevisionId:
196
                    revision_id = 'abc'
197
                    builder = tree.branch.get_commit_builder([],
198
                        revision_id=revision_id)
199
            except errors.CannotSetRevisionId:
200
                # This format doesn't support supplied revision ids
201
                return
202
            self.assertFalse(builder.random_revid)
203
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
204
                list(builder.record_iter_changes(tree, tree.last_revision(),
205
                    tree.iter_changes(tree.basis_tree())))
3775.2.6 by Robert Collins
CommitBuilder can specify a revision_id with record_iter_changes.
206
                builder.finish_inventory()
207
            except:
208
                builder.abort()
209
                raise
210
            self.assertEqual(revision_id, builder.commit('foo bar'))
211
        finally:
212
            tree.unlock()
213
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
214
        # the revision id must be set on the inventory when saving it. This
215
        # does not precisely test that - a repository that wants to can add it
216
        # on deserialisation, but thats all the current contract guarantees
217
        # anyway.
218
        self.assertEqual(revision_id,
219
            tree.branch.repository.get_inventory(revision_id).revision_id)
220
5222.1.1 by Aaron Bentley
Refuse to commit trees with no root.
221
    def test_commit_without_root_errors(self):
222
        tree = self.make_branch_and_tree(".")
223
        tree.lock_write()
224
        try:
225
            builder = tree.branch.get_commit_builder([])
226
            def do_commit():
227
                try:
228
                    list(builder.record_iter_changes(
229
                        tree, tree.last_revision(), []))
230
                    builder.finish_inventory()
231
                except:
232
                    builder.abort()
233
                    raise
234
            self.assertRaises(errors.RootMissing, do_commit)
235
        finally:
236
            tree.unlock()
237
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
238
    def test_commit_without_root_or_record_iter_changes_errors(self):
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
239
        tree = self.make_branch_and_tree(".")
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
240
        tree.lock_write()
241
        try:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
242
            self.build_tree(['foo'])
243
            tree.add('foo', 'foo-id')
5856.1.2 by Jelmer Vernooij
Delay accessing inventory in tests until necessary.
244
            builder = tree.branch.get_commit_builder([])
245
            if not builder.supports_record_entry_contents:
246
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
247
                    "record_entry_contents")
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
248
            entry = tree.inventory['foo-id']
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
249
            self.assertRaises(errors.RootMissing,
2776.4.4 by Robert Collins
Move content summary generation outside of record_entry_contents.
250
                builder.record_entry_contents, entry, [], 'foo', tree,
251
                    tree.path_content_summary('foo'))
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
252
            builder.abort()
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
253
        finally:
254
            tree.unlock()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
255
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
256
    def test_commit_unchanged_root_record_entry_contents(self):
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
257
        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
258
        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
259
        tree.lock_write()
260
        parent_tree = tree.basis_tree()
261
        parent_tree.lock_read()
262
        self.addCleanup(parent_tree.unlock)
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
263
        builder = tree.branch.get_commit_builder([old_revision_id])
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
264
        try:
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
265
            if not builder.supports_record_entry_contents:
266
                raise tests.TestNotApplicable("CommitBuilder doesn't support "
267
                    "record_entry_contents")
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
268
            ie = inventory.make_entry('directory', '', None,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
269
                    tree.get_root_id())
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
270
            delta, version_recorded, fs_hash = builder.record_entry_contents(
2776.4.13 by Robert Collins
Merge bzr.dev.
271
                ie, [parent_tree.inventory], '', tree,
2871.1.4 by Robert Collins
Merge bzr.dev.
272
                tree.path_content_summary(''))
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
273
            # Regardless of repository root behaviour we should consider this a
274
            # pointless commit.
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
275
            self.assertFalse(builder.any_changes())
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
276
            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
277
            # if the repository format recorded a new root revision, that
278
            # should be in the delta
279
            got_new_revision = ie.revision != old_revision_id
280
            if got_new_revision:
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
281
                self.assertEqual(('', '', ie.file_id, ie), delta)
282
                # The delta should be tracked
283
                self.assertEqual(delta, builder._basis_delta[-1])
2903.2.3 by Martin Pool
CommitBuilder tests should expect the root to be in the delta iff it's changed in the commit
284
            else:
285
                self.assertEqual(None, delta)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
286
            # Directories do not get hashed.
287
            self.assertEqual(None, fs_hash)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
288
            builder.abort()
289
        except:
290
            builder.abort()
291
            tree.unlock()
292
            raise
293
        else:
294
            tree.unlock()
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
295
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
296
    def test_commit_unchanged_root_record_iter_changes(self):
297
        tree = self.make_branch_and_tree(".")
298
        old_revision_id = tree.commit('')
299
        tree.lock_write()
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
300
        builder = tree.branch.get_commit_builder([old_revision_id])
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
301
        try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
302
            list(builder.record_iter_changes(tree, old_revision_id, []))
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
303
            # Regardless of repository root behaviour we should consider this a
304
            # pointless commit.
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
305
            self.assertFalse(builder.any_changes())
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
306
            builder.finish_inventory()
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
307
            builder_tree = builder.revision_tree()
308
            new_root_id = builder_tree.get_root_id()
309
            new_root_revision = builder_tree.get_file_revision(new_root_id)
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
310
            if tree.branch.repository.supports_rich_root():
311
                # We should not have seen a new root revision
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
312
                self.assertEqual(old_revision_id, new_root_revision)
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
313
            else:
314
                # We should see a new root revision
5878.1.1 by Jelmer Vernooij
Avoid inventory usage in a commit builder test.
315
                self.assertNotEqual(old_revision_id, new_root_revision)
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
316
        finally:
317
            builder.abort()
318
            tree.unlock()
319
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
320
    def test_commit_record_entry_contents(self):
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
321
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
322
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
323
        try:
324
            builder = tree.branch.get_commit_builder([])
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
325
            if not builder.supports_record_entry_contents:
326
                raise tests.TestNotApplicable("CommitBuilder doesn't "
327
                    "support record_entry_contents")
2617.6.8 by Robert Collins
Review feedback and documentation.
328
            self.record_root(builder, tree)
329
            builder.finish_inventory()
330
            rev_id = builder.commit('foo bar')
331
        finally:
332
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
333
        self.assertNotEqual(None, rev_id)
334
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
1757.1.2 by Robert Collins
Bugfix CommitBuilders recording of the inventory revision id.
335
        # the revision id must be set on the inventory when saving it. This does not
336
        # precisely test that - a repository that wants to can add it on deserialisation,
337
        # but thats all the current contract guarantees anyway.
338
        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
339
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
340
    def test_get_basis_delta(self):
341
        tree = self.make_branch_and_tree(".")
342
        self.build_tree(["foo"])
343
        tree.add(["foo"], ["foo-id"])
344
        old_revision_id = tree.commit("added foo")
345
        tree.lock_write()
346
        try:
347
            self.build_tree(['bar'])
348
            tree.add(['bar'], ['bar-id'])
349
            basis = tree.branch.repository.revision_tree(old_revision_id)
350
            basis.lock_read()
351
            self.addCleanup(basis.unlock)
352
            builder = tree.branch.get_commit_builder([old_revision_id])
353
            total_delta = []
354
            try:
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
355
                if not builder.supports_record_entry_contents:
356
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
357
                        "support record_entry_contents")
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
358
                parent_invs = [basis.inventory]
359
                builder.will_record_deletes()
360
                if builder.record_root_entry:
361
                    ie = basis.inventory.root.copy()
362
                    delta, _, _ = builder.record_entry_contents(ie, parent_invs,
363
                        '', tree, tree.path_content_summary(''))
364
                    if delta is not None:
365
                        total_delta.append(delta)
366
                delta = builder.record_delete("foo", "foo-id")
367
                total_delta.append(delta)
368
                new_bar = inventory.make_entry('file', 'bar',
369
                    parent_id=tree.get_root_id(), file_id='bar-id')
370
                delta, _, _ = builder.record_entry_contents(new_bar, parent_invs,
371
                    'bar', tree, tree.path_content_summary('bar'))
372
                total_delta.append(delta)
373
                # All actions should have been recorded in the basis_delta
374
                self.assertEqual(total_delta, builder.get_basis_delta())
375
                builder.finish_inventory()
376
                builder.commit('delete foo, add bar')
377
            except:
378
                tree.branch.repository.abort_write_group()
379
                raise
380
        finally:
381
            tree.unlock()
382
383
    def test_get_basis_delta_without_notification(self):
384
        tree = self.make_branch_and_tree(".")
385
        old_revision_id = tree.commit('')
386
        tree.lock_write()
387
        try:
388
            parent_tree = tree.basis_tree()
389
            parent_tree.lock_read()
390
            self.addCleanup(parent_tree.unlock)
391
            builder = tree.branch.get_commit_builder([old_revision_id])
392
            # It is an error to expect builder.get_basis_delta() to be correct,
393
            # if you have not also called will_record_deletes() to indicate you
394
            # will be calling record_delete() when appropriate
395
            self.assertRaises(AssertionError, builder.get_basis_delta)
396
            tree.branch.repository.abort_write_group()
397
        finally:
398
            tree.unlock()
399
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
400
    def test_record_delete(self):
401
        tree = self.make_branch_and_tree(".")
402
        self.build_tree(["foo"])
403
        tree.add(["foo"], ["foo-id"])
404
        rev_id = tree.commit("added foo")
405
        # Remove the inventory details for foo-id, because
406
        # record_entry_contents ends up copying root verbatim.
407
        tree.unversion(["foo-id"])
408
        tree.lock_write()
409
        try:
410
            basis = tree.branch.repository.revision_tree(rev_id)
411
            builder = tree.branch.get_commit_builder([rev_id])
412
            try:
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
413
                if not builder.supports_record_entry_contents:
414
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
415
                        "support record_entry_contents")
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
416
                builder.will_record_deletes()
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
417
                if builder.record_root_entry is True:
418
                    parent_invs = [basis.inventory]
419
                    del basis.inventory.root.children['foo']
420
                    builder.record_entry_contents(basis.inventory.root,
421
                        parent_invs, '', tree, tree.path_content_summary(''))
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
422
                # the delta should be returned, and recorded in _basis_delta
423
                delta = builder.record_delete("foo", "foo-id")
424
                self.assertEqual(("foo", None, "foo-id", None), delta)
425
                self.assertEqual(delta, builder._basis_delta[-1])
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
426
                builder.finish_inventory()
427
                rev_id2 = builder.commit('delete foo')
428
            except:
429
                tree.branch.repository.abort_write_group()
430
                raise
431
        finally:
432
            tree.unlock()
433
        rev_tree = builder.revision_tree()
434
        rev_tree.lock_read()
435
        self.addCleanup(rev_tree.unlock)
436
        self.assertFalse(rev_tree.path2id('foo'))
437
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
438
    def test_record_delete_record_iter_changes(self):
439
        tree = self.make_branch_and_tree(".")
440
        self.build_tree(["foo"])
441
        tree.add(["foo"], ["foo-id"])
442
        rev_id = tree.commit("added foo")
443
        tree.lock_write()
444
        try:
445
            builder = tree.branch.get_commit_builder([rev_id])
446
            try:
447
                delete_change = ('foo-id', ('foo', None), True, (True, False),
448
                    (tree.path2id(''), None), ('foo', None), ('file', None),
449
                    (False, None))
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
450
                list(builder.record_iter_changes(tree, rev_id,
451
                    [delete_change]))
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
452
                self.assertEqual(("foo", None, "foo-id", None),
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
453
                    builder._basis_delta[0])
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
454
                self.assertTrue(builder.any_changes())
455
                builder.finish_inventory()
456
                rev_id2 = builder.commit('delete foo')
457
            except:
458
                builder.abort()
459
                raise
460
        finally:
461
            tree.unlock()
462
        rev_tree = builder.revision_tree()
463
        rev_tree.lock_read()
464
        self.addCleanup(rev_tree.unlock)
465
        self.assertFalse(rev_tree.path2id('foo'))
466
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
467
    def test_record_delete_without_notification(self):
468
        tree = self.make_branch_and_tree(".")
469
        self.build_tree(["foo"])
470
        tree.add(["foo"], ["foo-id"])
471
        rev_id = tree.commit("added foo")
472
        tree.lock_write()
473
        try:
474
            builder = tree.branch.get_commit_builder([rev_id])
475
            try:
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
476
                if not builder.supports_record_entry_contents:
477
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
478
                        "support record_entry_contents")
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
479
                self.record_root(builder, tree)
480
                self.assertRaises(AssertionError,
481
                    builder.record_delete, "foo", "foo-id")
482
            finally:
483
                tree.branch.repository.abort_write_group()
484
        finally:
485
            tree.unlock()
486
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
487
    def test_revision_tree_record_entry_contents(self):
2041.1.1 by John Arbash Meinel
Add a 'get_tree()' call that returns a RevisionTree for the newly committed tree
488
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
489
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
490
        try:
491
            builder = tree.branch.get_commit_builder([])
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
492
            if not builder.supports_record_entry_contents:
493
                raise tests.TestNotApplicable("CommitBuilder doesn't "
494
                    "support record_entry_contents")
2617.6.8 by Robert Collins
Review feedback and documentation.
495
            self.record_root(builder, tree)
496
            builder.finish_inventory()
497
            rev_id = builder.commit('foo bar')
498
        finally:
499
            tree.unlock()
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
500
        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
501
        # Just a couple simple tests to ensure that it actually follows
502
        # the RevisionTree api.
503
        self.assertEqual(rev_id, rev_tree.get_revision_id())
504
        self.assertEqual([], rev_tree.get_parent_ids())
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
505
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
506
    def test_revision_tree_record_iter_changes(self):
507
        tree = self.make_branch_and_tree(".")
508
        tree.lock_write()
509
        try:
510
            builder = tree.branch.get_commit_builder([])
511
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
512
                list(builder.record_iter_changes(tree,
513
                    _mod_revision.NULL_REVISION,
514
                    tree.iter_changes(tree.basis_tree())))
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
515
                builder.finish_inventory()
516
                rev_id = builder.commit('foo bar')
517
            except:
518
                builder.abort()
519
                raise
520
            rev_tree = builder.revision_tree()
521
            # Just a couple simple tests to ensure that it actually follows
522
            # the RevisionTree api.
523
            self.assertEqual(rev_id, rev_tree.get_revision_id())
524
            self.assertEqual([], rev_tree.get_parent_ids())
525
        finally:
526
            tree.unlock()
527
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
528
    def test_root_entry_has_revision(self):
529
        # test the root revision created and put in the basis
530
        # has the right rev id.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
531
        # XXX: RBC 20081118 - this test is too big, it depends on the exact
532
        # behaviour of tree methods and so on; it should be written to the
533
        # commit builder interface directly.
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
534
        tree = self.make_branch_and_tree('.')
535
        rev_id = tree.commit('message')
536
        basis_tree = tree.basis_tree()
537
        basis_tree.lock_read()
538
        self.addCleanup(basis_tree.unlock)
5819.2.4 by Jelmer Vernooij
Avoid using inventory.
539
        self.assertEqual(rev_id,
540
            basis_tree.get_file_revision(basis_tree.get_root_id()))
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
541
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
542
    def _get_revtrees(self, tree, revision_ids):
2592.3.214 by Robert Collins
Merge bzr.dev.
543
        tree.lock_read()
544
        try:
545
            trees = list(tree.branch.repository.revision_trees(revision_ids))
546
            for _tree in trees:
547
                _tree.lock_read()
548
                self.addCleanup(_tree.unlock)
549
            return trees
550
        finally:
551
            tree.unlock()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
552
553
    def test_last_modified_revision_after_commit_root_unchanged(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
554
        # commiting without changing the root does not change the
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
555
        # last modified except on non-rich-root-repositories.
556
        tree = self.make_branch_and_tree('.')
557
        rev1 = tree.commit('')
558
        rev2 = tree.commit('')
559
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
5819.2.4 by Jelmer Vernooij
Avoid using inventory.
560
        self.assertEqual(rev1, tree1.get_file_revision(tree1.get_root_id()))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
561
        if tree.branch.repository.supports_rich_root():
5819.2.4 by Jelmer Vernooij
Avoid using inventory.
562
            self.assertEqual(rev1,
563
                tree2.get_file_revision(tree2.get_root_id()))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
564
        else:
5819.2.4 by Jelmer Vernooij
Avoid using inventory.
565
            self.assertEqual(rev2,
566
                tree2.get_file_revision(tree2.get_root_id()))
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
567
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
568
    def _add_commit_check_unchanged(self, tree, name, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
569
        tree.add([name], [name + 'id'])
4183.5.3 by Robert Collins
Fix typo.
570
        self._commit_check_unchanged(tree, name, name + 'id',
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
571
            mini_commit=mini_commit)
572
573
    def _commit_check_unchanged(self, tree, name, file_id, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
574
        rev1 = tree.commit('')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
575
        if mini_commit is None:
576
            mini_commit = self.mini_commit
577
        rev2 = 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.
578
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
579
        self.assertEqual(rev1, tree1.get_file_revision(file_id))
580
        self.assertEqual(rev1, tree2.get_file_revision(file_id))
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.
581
        expected_graph = {}
582
        expected_graph[(file_id, rev1)] = ()
583
        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.
584
585
    def test_last_modified_revision_after_commit_dir_unchanged(self):
586
        # committing without changing a dir does not change the last modified.
587
        tree = self.make_branch_and_tree('.')
588
        self.build_tree(['dir/'])
589
        self._add_commit_check_unchanged(tree, 'dir')
590
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
591
    def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
592
        # committing without changing a dir does not change the last modified.
593
        tree = self.make_branch_and_tree('.')
594
        self.build_tree(['dir/'])
595
        self._add_commit_check_unchanged(tree, 'dir',
596
            mini_commit=self.mini_commit_record_iter_changes)
597
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
598
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
599
        # committing without changing a dir does not change the last modified
600
        # of the dir even the dirs contents are changed.
601
        tree = self.make_branch_and_tree('.')
602
        self.build_tree(['dir/'])
603
        tree.add(['dir'], ['dirid'])
604
        rev1 = tree.commit('')
605
        self.build_tree(['dir/content'])
606
        tree.add(['dir/content'], ['contentid'])
607
        rev2 = tree.commit('')
608
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
609
        self.assertEqual(rev1, tree1.get_file_revision('dirid'))
610
        self.assertEqual(rev1, tree2.get_file_revision('dirid'))
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.
611
        file_id = 'dirid'
612
        expected_graph = {}
613
        expected_graph[(file_id, rev1)] = ()
614
        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.
615
616
    def test_last_modified_revision_after_commit_file_unchanged(self):
617
        # committing without changing a file does not change the last modified.
618
        tree = self.make_branch_and_tree('.')
619
        self.build_tree(['file'])
620
        self._add_commit_check_unchanged(tree, 'file')
621
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
622
    def test_last_modified_revision_after_commit_file_unchanged_ric(self):
623
        # committing without changing a file does not change the last modified.
624
        tree = self.make_branch_and_tree('.')
625
        self.build_tree(['file'])
626
        self._add_commit_check_unchanged(tree, 'file',
627
            mini_commit=self.mini_commit_record_iter_changes)
628
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
629
    def test_last_modified_revision_after_commit_link_unchanged(self):
630
        # committing without changing a link does not change the last modified.
631
        self.requireFeature(tests.SymlinkFeature)
632
        tree = self.make_branch_and_tree('.')
633
        os.symlink('target', 'link')
634
        self._add_commit_check_unchanged(tree, 'link')
635
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
636
    def test_last_modified_revision_after_commit_link_unchanged_ric(self):
4183.5.6 by Robert Collins
Review caught a bogus change to test_last_modified_revision_after_commit_link_unchanged_ric.
637
        # committing without changing a link does not change the last modified.
638
        self.requireFeature(tests.SymlinkFeature)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
639
        tree = self.make_branch_and_tree('.')
640
        os.symlink('target', 'link')
641
        self._add_commit_check_unchanged(tree, 'link',
642
            mini_commit=self.mini_commit_record_iter_changes)
643
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
644
    def test_last_modified_revision_after_commit_reference_unchanged(self):
645
        # committing without changing a subtree does not change the last
646
        # modified.
647
        tree = self.make_branch_and_tree('.')
648
        subtree = self.make_reference('reference')
649
        try:
650
            tree.add_reference(subtree)
651
            self._commit_check_unchanged(tree, 'reference',
652
                subtree.get_root_id())
653
        except errors.UnsupportedOperation:
654
            return
655
656
    def test_last_modified_revision_after_commit_reference_unchanged_ric(self):
657
        # committing without changing a subtree does not change the last
658
        # modified.
659
        tree = self.make_branch_and_tree('.')
660
        subtree = self.make_reference('reference')
661
        try:
662
            tree.add_reference(subtree)
663
            self._commit_check_unchanged(tree, 'reference',
664
                subtree.get_root_id(),
665
                mini_commit=self.mini_commit_record_iter_changes)
666
        except errors.UnsupportedOperation:
667
            return
668
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
669
    def _add_commit_renamed_check_changed(self, tree, name,
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
670
        expect_fs_hash=False, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
671
        def rename():
672
            tree.rename_one(name, 'new_' + name)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
673
        self._add_commit_change_check_changed(tree, name, rename,
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
674
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
675
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
676
    def _commit_renamed_check_changed(self, tree, name, file_id,
677
        expect_fs_hash=False, mini_commit=None):
678
        def rename():
679
            tree.rename_one(name, 'new_' + name)
680
        self._commit_change_check_changed(tree, name, file_id, rename,
681
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
682
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
683
    def test_last_modified_revision_after_rename_dir_changes(self):
684
        # renaming a dir changes the last modified.
685
        tree = self.make_branch_and_tree('.')
686
        self.build_tree(['dir/'])
687
        self._add_commit_renamed_check_changed(tree, 'dir')
688
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
689
    def test_last_modified_revision_after_rename_dir_changes_ric(self):
690
        # renaming a dir changes the last modified.
691
        tree = self.make_branch_and_tree('.')
692
        self.build_tree(['dir/'])
693
        self._add_commit_renamed_check_changed(tree, 'dir',
694
            mini_commit=self.mini_commit_record_iter_changes)
695
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
696
    def test_last_modified_revision_after_rename_file_changes(self):
697
        # renaming a file changes the last modified.
698
        tree = self.make_branch_and_tree('.')
699
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
700
        self._add_commit_renamed_check_changed(tree, 'file',
701
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
702
3775.2.12 by Robert Collins
CommitBuilder.record_iter_changes handles renamed files.
703
    def test_last_modified_revision_after_rename_file_changes_ric(self):
704
        # renaming a file changes the last modified.
705
        tree = self.make_branch_and_tree('.')
706
        self.build_tree(['file'])
707
        self._add_commit_renamed_check_changed(tree, 'file',
708
            expect_fs_hash=True,
709
            mini_commit=self.mini_commit_record_iter_changes)
710
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
711
    def test_last_modified_revision_after_rename_link_changes(self):
712
        # renaming a link changes the last modified.
713
        self.requireFeature(tests.SymlinkFeature)
714
        tree = self.make_branch_and_tree('.')
715
        os.symlink('target', 'link')
716
        self._add_commit_renamed_check_changed(tree, 'link')
717
3775.2.13 by Robert Collins
CommitBuilder.record_iter_changes handles renamed symlinks.
718
    def test_last_modified_revision_after_rename_link_changes_ric(self):
719
        # renaming a link changes the last modified.
720
        self.requireFeature(tests.SymlinkFeature)
721
        tree = self.make_branch_and_tree('.')
722
        os.symlink('target', 'link')
723
        self._add_commit_renamed_check_changed(tree, 'link',
724
            mini_commit=self.mini_commit_record_iter_changes)
725
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
726
    def test_last_modified_revision_after_rename_ref_changes(self):
727
        # renaming a reference changes the last modified.
728
        tree = self.make_branch_and_tree('.')
729
        subtree = self.make_reference('reference')
730
        try:
731
            tree.add_reference(subtree)
732
            self._commit_renamed_check_changed(tree, 'reference',
733
                subtree.get_root_id())
734
        except errors.UnsupportedOperation:
735
            return
736
737
    def test_last_modified_revision_after_rename_ref_changes_ric(self):
738
        # renaming a reference changes the last modified.
739
        tree = self.make_branch_and_tree('.')
740
        subtree = self.make_reference('reference')
741
        try:
742
            tree.add_reference(subtree)
743
            self._commit_renamed_check_changed(tree, 'reference',
744
                subtree.get_root_id(),
745
                mini_commit=self.mini_commit_record_iter_changes)
746
        except errors.UnsupportedOperation:
747
            return
748
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
749
    def _add_commit_reparent_check_changed(self, tree, name,
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
750
        expect_fs_hash=False, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
751
        self.build_tree(['newparent/'])
752
        tree.add(['newparent'])
753
        def reparent():
754
            tree.rename_one(name, 'newparent/new_' + name)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
755
        self._add_commit_change_check_changed(tree, name, reparent,
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
756
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
757
758
    def test_last_modified_revision_after_reparent_dir_changes(self):
759
        # reparenting a dir changes the last modified.
760
        tree = self.make_branch_and_tree('.')
761
        self.build_tree(['dir/'])
762
        self._add_commit_reparent_check_changed(tree, 'dir')
763
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
764
    def test_last_modified_revision_after_reparent_dir_changes_ric(self):
765
        # reparenting a dir changes the last modified.
766
        tree = self.make_branch_and_tree('.')
767
        self.build_tree(['dir/'])
768
        self._add_commit_reparent_check_changed(tree, 'dir',
769
            mini_commit=self.mini_commit_record_iter_changes)
770
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
771
    def test_last_modified_revision_after_reparent_file_changes(self):
772
        # reparenting a file changes the last modified.
773
        tree = self.make_branch_and_tree('.')
774
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
775
        self._add_commit_reparent_check_changed(tree, 'file',
776
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
777
3775.2.15 by Robert Collins
CommitBuilder.record_iter_changes handles reparented files.
778
    def test_last_modified_revision_after_reparent_file_changes_ric(self):
779
        # reparenting a file changes the last modified.
780
        tree = self.make_branch_and_tree('.')
781
        self.build_tree(['file'])
782
        self._add_commit_reparent_check_changed(tree, 'file',
3775.2.17 by Robert Collins
CommitBuilder.record_iter_changes handles changed files.
783
            expect_fs_hash=True,
784
            mini_commit=self.mini_commit_record_iter_changes)
3775.2.15 by Robert Collins
CommitBuilder.record_iter_changes handles reparented files.
785
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
786
    def test_last_modified_revision_after_reparent_link_changes(self):
787
        # reparenting a link changes the last modified.
788
        self.requireFeature(tests.SymlinkFeature)
789
        tree = self.make_branch_and_tree('.')
790
        os.symlink('target', 'link')
791
        self._add_commit_reparent_check_changed(tree, 'link')
792
3775.2.16 by Robert Collins
CommitBuilder.record_iter_changes handles reparented symlinks.
793
    def test_last_modified_revision_after_reparent_link_changes_ric(self):
794
        # reparenting a link changes the last modified.
795
        self.requireFeature(tests.SymlinkFeature)
796
        tree = self.make_branch_and_tree('.')
797
        os.symlink('target', 'link')
798
        self._add_commit_reparent_check_changed(tree, 'link',
799
            mini_commit=self.mini_commit_record_iter_changes)
800
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
801
    def _add_commit_change_check_changed(self, tree, name, changer,
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
802
        expect_fs_hash=False, mini_commit=None, file_id=None):
803
        if file_id is None:
804
            file_id = name + 'id'
805
        tree.add([name], [file_id])
806
        self._commit_change_check_changed(
807
            tree, name, file_id,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
808
            changer, expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
809
4183.5.3 by Robert Collins
Fix typo.
810
    def _commit_change_check_changed(self, tree, name, file_id, changer,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
811
        expect_fs_hash=False, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
812
        rev1 = tree.commit('')
813
        changer()
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
814
        if mini_commit is None:
815
            mini_commit = self.mini_commit
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
816
        rev2 = mini_commit(tree, name, tree.id2path(file_id),
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
817
            expect_fs_hash=expect_fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
818
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
819
        self.assertEqual(rev1, tree1.get_file_revision(file_id))
820
        self.assertEqual(rev2, tree2.get_file_revision(file_id))
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.
821
        expected_graph = {}
822
        expected_graph[(file_id, rev1)] = ()
823
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
824
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
825
826
    def mini_commit(self, tree, name, new_name, records_version=True,
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
827
        delta_against_basis=True, expect_fs_hash=False):
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
828
        """Perform a miniature commit looking for record entry results.
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
829
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
830
        :param tree: The tree to commit.
831
        :param name: The path in the basis tree of the tree being committed.
832
        :param new_name: The path in the tree being committed.
833
        :param records_version: True if the commit of new_name is expected to
834
            record a new version.
835
        :param delta_against_basis: True of the commit of new_name is expected
836
            to have a delta against the basis.
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
837
        :param expect_fs_hash: True or false to indicate whether we expect a
838
            file hash to be returned from the record_entry_contents call.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
839
        """
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
840
        tree.lock_write()
841
        try:
842
            # mini manual commit here so we can check the return of
843
            # record_entry_contents.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
844
            parent_ids = tree.get_parent_ids()
845
            builder = tree.branch.get_commit_builder(parent_ids)
5707.1.1 by Jelmer Vernooij
Properly try/except.
846
            try:
5718.4.3 by Jelmer Vernooij
Skip record_entry_contents-using tests for commit builders that don't support it.
847
                if not builder.supports_record_entry_contents:
848
                    raise tests.TestNotApplicable("CommitBuilder doesn't "
849
                        "support record_entry_contents")
5707.1.1 by Jelmer Vernooij
Properly try/except.
850
                parent_tree = tree.basis_tree()
851
                parent_tree.lock_read()
852
                self.addCleanup(parent_tree.unlock)
853
                parent_invs = [parent_tree.inventory]
854
                for parent_id in parent_ids[1:]:
855
                    parent_invs.append(tree.branch.repository.revision_tree(
856
                        parent_id).inventory)
857
                # root
858
                builder.record_entry_contents(
859
                    inventory.make_entry('directory', '', None,
860
                        tree.get_root_id()), parent_invs, '', tree,
861
                        tree.path_content_summary(''))
862
                def commit_id(file_id):
863
                    old_ie = tree.inventory[file_id]
864
                    path = tree.id2path(file_id)
865
                    ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
866
                        old_ie.parent_id, file_id)
867
                    content_summary = tree.path_content_summary(path)
868
                    if content_summary[0] == 'tree-reference':
869
                        content_summary = content_summary[:3] + (
870
                            tree.get_reference_revision(file_id),)
871
                    return builder.record_entry_contents(ie, parent_invs, path,
872
                        tree, content_summary)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
873
5707.1.1 by Jelmer Vernooij
Properly try/except.
874
                file_id = tree.path2id(new_name)
875
                parent_id = tree.inventory[file_id].parent_id
876
                if parent_id != tree.get_root_id():
877
                    commit_id(parent_id)
878
                # because a change of some sort is meant to have occurred,
879
                # recording the entry must return True.
880
                delta, version_recorded, fs_hash = commit_id(file_id)
881
                if records_version:
882
                    self.assertTrue(version_recorded)
883
                else:
884
                    self.assertFalse(version_recorded)
885
                if expect_fs_hash:
886
                    tree_file_stat = tree.get_file_with_stat(file_id)
887
                    tree_file_stat[0].close()
888
                    self.assertEqual(2, len(fs_hash))
889
                    self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
890
                    self.assertEqualStat(tree_file_stat[1], fs_hash[1])
891
                else:
892
                    self.assertEqual(None, fs_hash)
893
                new_entry = builder.new_inventory[file_id]
894
                if delta_against_basis:
895
                    expected_delta = (name, new_name, file_id, new_entry)
896
                    # The delta should be recorded
897
                    self.assertEqual(expected_delta, builder._basis_delta[-1])
898
                else:
899
                    expected_delta = None
900
                self.assertEqual(expected_delta, delta)
901
                builder.finish_inventory()
902
            except:
903
                builder.abort()
904
                raise
905
            else:
906
                rev2 = builder.commit('')
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
907
        except:
908
            tree.unlock()
909
            raise
4245.1.1 by Ian Clatworthy
minor test clean-ups & _reconcile_pack API
910
        try:
911
            tree.set_parent_ids([rev2])
912
        finally:
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
913
            tree.unlock()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
914
        return rev2
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
915
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
916
    def mini_commit_record_iter_changes(self, tree, name, new_name,
917
        records_version=True, delta_against_basis=True, expect_fs_hash=False):
918
        """Perform a miniature commit looking for record entry results.
919
920
        This version uses the record_iter_changes interface.
921
        
922
        :param tree: The tree to commit.
923
        :param name: The path in the basis tree of the tree being committed.
924
        :param new_name: The path in the tree being committed.
925
        :param records_version: True if the commit of new_name is expected to
926
            record a new version.
927
        :param delta_against_basis: True of the commit of new_name is expected
928
            to have a delta against the basis.
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
929
        :param expect_fs_hash: If true, looks for a fs hash output from
930
            record_iter_changes.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
931
        """
932
        tree.lock_write()
933
        try:
934
            # mini manual commit here so we can check the return of
935
            # record_entry_contents.
936
            parent_ids = tree.get_parent_ids()
937
            builder = tree.branch.get_commit_builder(parent_ids)
938
            parent_tree = tree.basis_tree()
939
            parent_tree.lock_read()
940
            self.addCleanup(parent_tree.unlock)
5856.1.2 by Jelmer Vernooij
Delay accessing inventory in tests until necessary.
941
            parent_trees = [parent_tree]
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
942
            for parent_id in parent_ids[1:]:
5856.1.2 by Jelmer Vernooij
Delay accessing inventory in tests until necessary.
943
                parent_trees.append(tree.branch.repository.revision_tree(
944
                    parent_id))
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
945
            changes = list(tree.iter_changes(parent_tree))
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
946
            result = list(builder.record_iter_changes(tree, parent_ids[0],
947
                changes))
948
            file_id = tree.path2id(new_name)
949
            if expect_fs_hash:
950
                tree_file_stat = tree.get_file_with_stat(file_id)
951
                tree_file_stat[0].close()
952
                self.assertLength(1, result)
953
                result = result[0]
954
                self.assertEqual(result[:2], (file_id, new_name))
955
                self.assertEqual(result[2][0], tree.get_file_sha1(file_id))
956
                self.assertEqualStat(result[2][1], tree_file_stat[1])
957
            else:
958
                self.assertEqual([], result)
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
959
            delta = builder._basis_delta
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
960
            delta_dict = dict((change[2], change) for change in delta)
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
961
            version_recorded = (file_id in delta_dict and
962
                delta_dict[file_id][3] is not None and
963
                delta_dict[file_id][3].revision == builder._new_revision_id)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
964
            if records_version:
965
                self.assertTrue(version_recorded)
966
            else:
967
                self.assertFalse(version_recorded)
4789.27.2 by John Arbash Meinel
Add some tests that the record-iter-changes is setting inv_sha1 correctly.
968
            self.assertIs(None, builder.new_inventory)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
969
            builder.finish_inventory()
5718.4.4 by Jelmer Vernooij
Only check .inventories if present.
970
            if tree.branch.repository._format.supports_full_versioned_files:
971
                inv_key = (builder._new_revision_id,)
972
                inv_sha1 = tree.branch.repository.inventories.get_sha1s(
973
                                [inv_key])[inv_key]
974
                self.assertEqual(inv_sha1, builder.inv_sha1)
4789.27.4 by John Arbash Meinel
Robert says that self.new_inventory shouldn't be set.
975
            self.assertIs(None, builder.new_inventory)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
976
            new_inventory = builder.revision_tree().inventory
977
            new_entry = new_inventory[file_id]
978
            if delta_against_basis:
979
                expected_delta = (name, new_name, file_id, new_entry)
980
                self.assertEqual(expected_delta, delta_dict[file_id])
981
            else:
982
                expected_delta = None
983
                self.assertFalse(version_recorded)
984
            rev2 = builder.commit('')
985
            tree.set_parent_ids([rev2])
986
        except:
987
            builder.abort()
988
            tree.unlock()
989
            raise
990
        else:
991
            tree.unlock()
992
        return rev2
993
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.
994
    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.
995
        # all the changes that have occured should be in the ancestry
996
        # (closest to a public per-file graph API we have today)
997
        tree.lock_read()
998
        self.addCleanup(tree.unlock)
5815.5.9 by Jelmer Vernooij
Remove dependencies on texts.
999
        g = dict(tree.branch.repository.get_file_graph().iter_ancestry([tip]))
5010.2.24 by Vincent Ladeuil
Fix imports in per_repository/test_commit_builder.py.
1000
        self.assertEqual(expected_graph, g)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1001
1002
    def test_last_modified_revision_after_content_file_changes(self):
1003
        # altering a file changes the last modified.
1004
        tree = self.make_branch_and_tree('.')
1005
        self.build_tree(['file'])
1006
        def change_file():
1007
            tree.put_file_bytes_non_atomic('fileid', 'new content')
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1008
        self._add_commit_change_check_changed(tree, 'file', change_file,
1009
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1010
3775.2.17 by Robert Collins
CommitBuilder.record_iter_changes handles changed files.
1011
    def test_last_modified_revision_after_content_file_changes_ric(self):
1012
        # altering a file changes the last modified.
1013
        tree = self.make_branch_and_tree('.')
1014
        self.build_tree(['file'])
1015
        def change_file():
1016
            tree.put_file_bytes_non_atomic('fileid', 'new content')
1017
        self._add_commit_change_check_changed(tree, 'file', change_file,
1018
            expect_fs_hash=True,
1019
            mini_commit=self.mini_commit_record_iter_changes)
1020
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1021
    def test_last_modified_revision_after_content_link_changes(self):
1022
        # changing a link changes the last modified.
1023
        self.requireFeature(tests.SymlinkFeature)
1024
        tree = self.make_branch_and_tree('.')
1025
        os.symlink('target', 'link')
1026
        def change_link():
1027
            os.unlink('link')
1028
            os.symlink('newtarget', 'link')
1029
        self._add_commit_change_check_changed(tree, 'link', change_link)
1030
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
1031
    def _test_last_mod_rev_after_content_link_changes_ric(
1032
        self, link, target, newtarget, file_id=None):
1033
        if file_id is None:
1034
            file_id = link
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
1035
        # changing a link changes the last modified.
1036
        self.requireFeature(tests.SymlinkFeature)
1037
        tree = self.make_branch_and_tree('.')
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
1038
        os.symlink(target, link)
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
1039
        def change_link():
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
1040
            os.unlink(link)
1041
            os.symlink(newtarget, link)
1042
        self._add_commit_change_check_changed(
1043
            tree, link, change_link,
1044
            mini_commit=self.mini_commit_record_iter_changes,
1045
            file_id=file_id)
1046
1047
    def test_last_modified_rev_after_content_link_changes_ric(self):
1048
        self._test_last_mod_rev_after_content_link_changes_ric(
1049
            'link', 'target', 'newtarget')
1050
1051
    def test_last_modified_rev_after_content_unicode_link_changes_ric(self):
1052
        self.requireFeature(tests.UnicodeFilenameFeature)
1053
        self._test_last_mod_rev_after_content_link_changes_ric(
1054
            u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget',
1055
1056
            file_id=u'li\u1234nk'.encode('UTF-8'))
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
1057
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1058
    def _commit_sprout(self, tree, name):
1059
        tree.add([name], [name + 'id'])
1060
        rev_id = tree.commit('')
1061
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
1062
1063
    def _rename_in_tree(self, tree, name):
1064
        tree.rename_one(name, 'new_' + name)
1065
        return tree.commit('')
1066
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
1067
    def _commit_sprout_rename_merge(self, tree1, name, expect_fs_hash=False,
1068
        mini_commit=None):
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1069
        """Do a rename in both trees."""
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1070
        rev1, tree2 = self._commit_sprout(tree1, name)
1071
        # change both sides equally
1072
        rev2 = self._rename_in_tree(tree1, name)
1073
        rev3 = self._rename_in_tree(tree2, name)
1074
        tree1.merge_from_branch(tree2.branch)
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
1075
        if mini_commit is None:
1076
            mini_commit = self.mini_commit
1077
        rev4 = mini_commit(tree1, 'new_' + name, 'new_' + name,
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1078
            expect_fs_hash=expect_fs_hash)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1079
        tree3, = self._get_revtrees(tree1, [rev4])
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
1080
        self.assertEqual(rev4, tree3.get_file_revision(name + 'id'))
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.
1081
        file_id = name + 'id'
1082
        expected_graph = {}
1083
        expected_graph[(file_id, rev1)] = ()
1084
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
1085
        expected_graph[(file_id, rev3)] = ((file_id, rev1),)
1086
        expected_graph[(file_id, rev4)] = ((file_id, rev2), (file_id, rev3),)
1087
        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.
1088
1089
    def test_last_modified_revision_after_merge_dir_changes(self):
1090
        # merge a dir changes the last modified.
1091
        tree1 = self.make_branch_and_tree('t1')
1092
        self.build_tree(['t1/dir/'])
1093
        self._commit_sprout_rename_merge(tree1, 'dir')
1094
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
1095
    def test_last_modified_revision_after_merge_dir_changes_ric(self):
1096
        # merge a dir changes the last modified.
1097
        tree1 = self.make_branch_and_tree('t1')
1098
        self.build_tree(['t1/dir/'])
1099
        self._commit_sprout_rename_merge(tree1, 'dir',
1100
            mini_commit=self.mini_commit_record_iter_changes)
1101
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1102
    def test_last_modified_revision_after_merge_file_changes(self):
1103
        # merge a file changes the last modified.
1104
        tree1 = self.make_branch_and_tree('t1')
1105
        self.build_tree(['t1/file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1106
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1107
3775.2.20 by Robert Collins
CommitBuilder.record_iter_changes handles merged files.
1108
    def test_last_modified_revision_after_merge_file_changes_ric(self):
1109
        # merge a file changes the last modified.
1110
        tree1 = self.make_branch_and_tree('t1')
1111
        self.build_tree(['t1/file'])
1112
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True,
1113
            mini_commit=self.mini_commit_record_iter_changes)
1114
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1115
    def test_last_modified_revision_after_merge_link_changes(self):
1116
        # merge a link changes the last modified.
1117
        self.requireFeature(tests.SymlinkFeature)
1118
        tree1 = self.make_branch_and_tree('t1')
1119
        os.symlink('target', 't1/link')
1120
        self._commit_sprout_rename_merge(tree1, 'link')
1121
3775.2.21 by Robert Collins
CommitBuilder.record_iter_changes handles merged symlinks.
1122
    def test_last_modified_revision_after_merge_link_changes_ric(self):
1123
        # merge a link changes the last modified.
1124
        self.requireFeature(tests.SymlinkFeature)
1125
        tree1 = self.make_branch_and_tree('t1')
1126
        os.symlink('target', 't1/link')
1127
        self._commit_sprout_rename_merge(tree1, 'link',
1128
            mini_commit=self.mini_commit_record_iter_changes)
1129
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1130
    def _commit_sprout_rename_merge_converged(self, tree1, name,
1131
        mini_commit=None):
1132
        # Make a merge which just incorporates a change from a branch:
1133
        # The per-file graph is straight line, and no alteration occurs
1134
        # in the inventory.
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
1135
        # Part 1: change in the merged branch.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1136
        rev1, tree2 = self._commit_sprout(tree1, name)
1137
        # change on the other side to merge back
1138
        rev2 = self._rename_in_tree(tree2, name)
1139
        tree1.merge_from_branch(tree2.branch)
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1140
        if mini_commit is None:
1141
            mini_commit = self.mini_commit
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
1142
        def _check_graph(in_tree, changed_in_tree):
1143
            rev3 = mini_commit(in_tree, name, 'new_' + name, False,
1144
                delta_against_basis=changed_in_tree)
1145
            tree3, = self._get_revtrees(in_tree, [rev2])
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
1146
            self.assertEqual(rev2, tree3.get_file_revision(name + 'id'))
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
1147
            file_id = name + 'id'
1148
            expected_graph = {}
1149
            expected_graph[(file_id, rev1)] = ()
1150
            expected_graph[(file_id, rev2)] = ((file_id, rev1),)
1151
            self.assertFileGraph(expected_graph, in_tree, (file_id, rev2))
1152
        _check_graph(tree1, True)
1153
        # Part 2: change in the merged into branch - we use tree2 that has a
1154
        # change to name, branch tree1 and give it an unrelated change, then
1155
        # merge that to t2.
1156
        other_tree = tree1.bzrdir.sprout('t3').open_workingtree()
1157
        other_rev = other_tree.commit('')
1158
        tree2.merge_from_branch(other_tree.branch)
1159
        _check_graph(tree2, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1160
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1161
    def _commit_sprout_make_merge(self, tree1, make, mini_commit=None):
1162
        # Make a merge which incorporates the addition of a new object to
1163
        # another branch. The per-file graph shows no additional change
1164
        # in the merge because its a straight line.
1165
        rev1 = tree1.commit('')
1166
        tree2 = tree1.bzrdir.sprout('t2').open_workingtree()
1167
        # make and commit on the other side to merge back
1168
        make('t2/name')
1169
        file_id = 'nameid'
1170
        tree2.add(['name'], [file_id])
1171
        rev2 = tree2.commit('')
1172
        tree1.merge_from_branch(tree2.branch)
1173
        if mini_commit is None:
1174
            mini_commit = self.mini_commit
1175
        rev3 = mini_commit(tree1, None, 'name', False)
1176
        tree3, = self._get_revtrees(tree1, [rev2])
1177
        # in rev2, name should be only changed in rev2
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
1178
        self.assertEqual(rev2, tree3.get_file_revision(file_id))
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1179
        expected_graph = {}
1180
        expected_graph[(file_id, rev2)] = ()
1181
        self.assertFileGraph(expected_graph, tree1, (file_id, rev2))
1182
1183
    def test_last_modified_revision_after_converged_merge_dir_unchanged(self):
1184
        # merge a dir that changed preserves the last modified.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1185
        tree1 = self.make_branch_and_tree('t1')
1186
        self.build_tree(['t1/dir/'])
1187
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
1188
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1189
    def test_last_modified_revision_after_converged_merge_dir_unchanged_ric(self):
1190
        # merge a dir that changed preserves the last modified.
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1191
        tree1 = self.make_branch_and_tree('t1')
1192
        self.build_tree(['t1/dir/'])
1193
        self._commit_sprout_rename_merge_converged(tree1, 'dir',
1194
            mini_commit=self.mini_commit_record_iter_changes)
1195
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1196
    def test_last_modified_revision_after_converged_merge_file_unchanged(self):
1197
        # merge a file that changed preserves the last modified.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1198
        tree1 = self.make_branch_and_tree('t1')
1199
        self.build_tree(['t1/file'])
1200
        self._commit_sprout_rename_merge_converged(tree1, 'file')
1201
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1202
    def test_last_modified_revision_after_converged_merge_file_unchanged_ric(self):
1203
        # merge a file that changed preserves the last modified.
3775.2.23 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch files.
1204
        tree1 = self.make_branch_and_tree('t1')
1205
        self.build_tree(['t1/file'])
1206
        self._commit_sprout_rename_merge_converged(tree1, 'file',
1207
            mini_commit=self.mini_commit_record_iter_changes)
1208
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1209
    def test_last_modified_revision_after_converged_merge_link_unchanged(self):
1210
        # merge a link that changed preserves the last modified.
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1211
        self.requireFeature(tests.SymlinkFeature)
1212
        tree1 = self.make_branch_and_tree('t1')
1213
        os.symlink('target', 't1/link')
1214
        self._commit_sprout_rename_merge_converged(tree1, 'link')
1215
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1216
    def test_last_modified_revision_after_converged_merge_link_unchanged_ric(self):
1217
        # merge a link that changed preserves the last modified.
3775.2.24 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch symlinks.
1218
        self.requireFeature(tests.SymlinkFeature)
1219
        tree1 = self.make_branch_and_tree('t1')
1220
        os.symlink('target', 't1/link')
1221
        self._commit_sprout_rename_merge_converged(tree1, 'link',
1222
            mini_commit=self.mini_commit_record_iter_changes)
1223
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1224
    def test_last_modified_revision_after_merge_new_dir_unchanged(self):
1225
        # merge a new dir does not change the last modified.
1226
        tree1 = self.make_branch_and_tree('t1')
1227
        self._commit_sprout_make_merge(tree1, self.make_dir)
1228
1229
    def test_last_modified_revision_after_merge_new_dir_unchanged_ric(self):
1230
        # merge a new dir does not change the last modified.
1231
        tree1 = self.make_branch_and_tree('t1')
1232
        self._commit_sprout_make_merge(tree1, self.make_dir,
1233
            mini_commit=self.mini_commit_record_iter_changes)
1234
1235
    def test_last_modified_revision_after_merge_new_file_unchanged(self):
1236
        # merge a new file does not change the last modified.
1237
        tree1 = self.make_branch_and_tree('t1')
1238
        self._commit_sprout_make_merge(tree1, self.make_file)
1239
1240
    def test_last_modified_revision_after_merge_new_file_unchanged_ric(self):
1241
        # merge a new file does not change the last modified.
1242
        tree1 = self.make_branch_and_tree('t1')
1243
        self._commit_sprout_make_merge(tree1, self.make_file,
1244
            mini_commit=self.mini_commit_record_iter_changes)
1245
1246
    def test_last_modified_revision_after_merge_new_link_unchanged(self):
1247
        # merge a new link does not change the last modified.
1248
        tree1 = self.make_branch_and_tree('t1')
1249
        self._commit_sprout_make_merge(tree1, self.make_link)
1250
1251
    def test_last_modified_revision_after_merge_new_link_unchanged_ric(self):
1252
        # merge a new link does not change the last modified.
1253
        tree1 = self.make_branch_and_tree('t1')
1254
        self._commit_sprout_make_merge(tree1, self.make_link,
1255
            mini_commit=self.mini_commit_record_iter_changes)
1256
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1257
    def make_dir(self, name):
1258
        self.build_tree([name + '/'])
1259
1260
    def make_file(self, name):
1261
        self.build_tree([name])
1262
1263
    def make_link(self, name):
1264
        self.requireFeature(tests.SymlinkFeature)
1265
        os.symlink('target', name)
1266
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
1267
    def make_reference(self, name):
1268
        tree = self.make_branch_and_tree(name, format='1.9-rich-root')
1269
        tree.commit('foo')
1270
        return tree
1271
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1272
    def _check_kind_change(self, make_before, make_after, expect_fs_hash=False,
1273
        mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1274
        tree = self.make_branch_and_tree('.')
1275
        path = 'name'
1276
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
1277
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1278
        def change_kind():
2831.5.2 by Vincent Ladeuil
Review feedback.
1279
            osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1280
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
1281
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1282
        self._add_commit_change_check_changed(tree, path, change_kind,
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1283
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1284
1285
    def test_last_modified_dir_file(self):
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1286
        self._check_kind_change(self.make_dir, self.make_file,
1287
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1288
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1289
    def test_last_modified_dir_file_ric(self):
1290
        self._check_kind_change(self.make_dir, self.make_file,
1291
            expect_fs_hash=True,
1292
            mini_commit=self.mini_commit_record_iter_changes)
1293
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1294
    def test_last_modified_dir_link(self):
1295
        self._check_kind_change(self.make_dir, self.make_link)
1296
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1297
    def test_last_modified_dir_link_ric(self):
1298
        self._check_kind_change(self.make_dir, self.make_link,
1299
            mini_commit=self.mini_commit_record_iter_changes)
1300
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1301
    def test_last_modified_link_file(self):
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1302
        self._check_kind_change(self.make_link, self.make_file,
1303
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1304
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
1305
    def test_last_modified_link_file_ric(self):
1306
        self._check_kind_change(self.make_link, self.make_file,
1307
            expect_fs_hash=True,
1308
            mini_commit=self.mini_commit_record_iter_changes)
1309
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1310
    def test_last_modified_link_dir(self):
1311
        self._check_kind_change(self.make_link, self.make_dir)
1312
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
1313
    def test_last_modified_link_dir_ric(self):
1314
        self._check_kind_change(self.make_link, self.make_dir,
1315
            mini_commit=self.mini_commit_record_iter_changes)
1316
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1317
    def test_last_modified_file_dir(self):
1318
        self._check_kind_change(self.make_file, self.make_dir)
1319
3775.2.27 by Robert Collins
CommitBuilder.record_iter_changes handles files becoming directories and links.
1320
    def test_last_modified_file_dir_ric(self):
1321
        self._check_kind_change(self.make_file, self.make_dir,
1322
            mini_commit=self.mini_commit_record_iter_changes)
1323
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1324
    def test_last_modified_file_link(self):
1325
        self._check_kind_change(self.make_file, self.make_link)
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
1326
3775.2.27 by Robert Collins
CommitBuilder.record_iter_changes handles files becoming directories and links.
1327
    def test_last_modified_file_link_ric(self):
1328
        self._check_kind_change(self.make_file, self.make_link,
1329
            mini_commit=self.mini_commit_record_iter_changes)
3775.2.28 by Robert Collins
Merge .dev.
1330
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
1331
    def test_get_commit_builder_with_invalid_revprops(self):
1332
        branch = self.make_branch('.')
1333
        branch.repository.lock_write()
1334
        self.addCleanup(branch.repository.unlock)
1335
        self.assertRaises(ValueError, branch.repository.get_commit_builder,
1336
            branch, [], branch.get_config(),
1337
            revprops={'invalid': u'property\rwith\r\ninvalid chars'})
1338
1339
    def test_commit_builder_commit_with_invalid_message(self):
1340
        branch = self.make_branch('.')
1341
        branch.repository.lock_write()
1342
        self.addCleanup(branch.repository.unlock)
1343
        builder = branch.repository.get_commit_builder(branch, [],
1344
            branch.get_config())
1345
        self.addCleanup(branch.repository.abort_write_group)
1346
        self.assertRaises(ValueError, builder.commit,
1347
            u'Invalid\r\ncommit message\r\n')
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
1348
5485.4.2 by Martin
Move guard to CommitBuilder.__init__ and test to bt.per_repository
1349
    def test_non_ascii_str_committer_rejected(self):
1350
        """Ensure an error is raised on a non-ascii byte string committer"""
1351
        branch = self.make_branch('.')
1352
        branch.repository.lock_write()
1353
        self.addCleanup(branch.repository.unlock)
1354
        self.assertRaises(UnicodeDecodeError,
1355
            branch.repository.get_commit_builder,
1356
            branch, [], branch.get_config(),
1357
            committer="Erik B\xe5gfors <erik@example.com>")
1358
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
1359
    def test_stacked_repositories_reject_commit_builder(self):
1360
        # As per bug 375013, committing to stacked repositories is currently
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
1361
        # broken if we aren't in a chk repository. So old repositories with
1362
        # fallbacks refuse to hand out a commit builder.
4595.4.2 by Robert Collins
Disable commit builders on stacked repositories.
1363
        repo_basis = self.make_repository('basis')
1364
        branch = self.make_branch('local')
1365
        repo_local = branch.repository
1366
        try:
1367
            repo_local.add_fallback_repository(repo_basis)
1368
        except errors.UnstackableRepositoryFormat:
1369
            raise tests.TestNotApplicable("not a stackable format.")
5557.1.17 by John Arbash Meinel
Fix the test case that was checking we refused to create a commit_builder
1370
        self.addCleanup(repo_local.lock_write().unlock)
1371
        if not repo_local._format.supports_chks:
1372
            self.assertRaises(errors.BzrError, repo_local.get_commit_builder,
1373
                branch, [], branch.get_config())
1374
        else:
1375
            builder = repo_local.get_commit_builder(branch, [],
1376
                                                    branch.get_config())
1377
            builder.abort()
5050.18.1 by Aaron Bentley
CommitBuilder user committer, not username in revision-id.
1378
1379
    def test_committer_no_username(self):
1380
        # Ensure that when no username is available but a committer is
1381
        # supplied, commit works.
5570.3.9 by Vincent Ladeuil
More use cases for overrideEnv, _cleanEnvironment *may* contain too much variables now.
1382
        self.overrideEnv('EMAIL', None)
5609.31.2 by mbp at sourcefrog
Also turn off whoami inference in per_repository tests
1383
        self.overrideEnv('BZR_EMAIL', None)
1384
        # Also, make sure that it's not inferred from mailname.
1385
        self.overrideAttr(config, '_auto_user_id',
1386
            lambda: (None, None))
5050.18.1 by Aaron Bentley
CommitBuilder user committer, not username in revision-id.
1387
        tree = self.make_branch_and_tree(".")
1388
        tree.lock_write()
1389
        try:
1390
            # Make sure no username is available.
1391
            self.assertRaises(errors.NoWhoami, tree.branch.get_commit_builder,
1392
                              [])
1393
            builder = tree.branch.get_commit_builder(
1394
                [], committer='me@example.com')
1395
            try:
1396
                list(builder.record_iter_changes(tree, tree.last_revision(),
1397
                    tree.iter_changes(tree.basis_tree())))
1398
                builder.finish_inventory()
1399
            except:
1400
                builder.abort()
1401
                raise
1402
            repo = tree.branch.repository
1403
            repo.commit_write_group()
1404
        finally:
1405
            tree.unlock()