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