/brz/remove-bazaar

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