~bzr-pqm/bzr/bzr.dev

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