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