/brz/remove-bazaar

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