/brz/remove-bazaar

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