/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
            self.assertEqual(revision_id, builder.commit('foo bar'))
189
        finally:
190
            tree.unlock()
191
        self.assertTrue(tree.branch.repository.has_revision(revision_id))
192
        # the revision id must be set on the inventory when saving it. This
193
        # does not precisely test that - a repository that wants to can add it
194
        # on deserialisation, but thats all the current contract guarantees
195
        # anyway.
196
        self.assertEqual(revision_id,
197
            tree.branch.repository.get_inventory(revision_id).revision_id)
198
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
199
    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
200
        tree = self.make_branch_and_tree(".")
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
201
        tree.lock_write()
202
        try:
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
203
            self.build_tree(['foo'])
204
            tree.add('foo', 'foo-id')
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
205
            entry = tree.inventory['foo-id']
206
            builder = tree.branch.get_commit_builder([])
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
207
            self.assertRaises(errors.RootMissing,
2776.4.4 by Robert Collins
Move content summary generation outside of record_entry_contents.
208
                builder.record_entry_contents, entry, [], 'foo', tree,
209
                    tree.path_content_summary('foo'))
2871.1.2 by Robert Collins
* ``CommitBuilder.record_entry_contents`` now requires the root entry of a
210
            builder.abort()
2255.7.8 by John Arbash Meinel
Lock the tree when using a commit builder.
211
        finally:
212
            tree.unlock()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
213
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
214
    def test_commit_unchanged_root(self):
215
        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
216
        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
217
        tree.lock_write()
218
        parent_tree = tree.basis_tree()
219
        parent_tree.lock_read()
220
        self.addCleanup(parent_tree.unlock)
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
221
        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
222
        try:
223
            ie = inventory.make_entry('directory', '', None,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
224
                    tree.get_root_id())
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
225
            delta, version_recorded, fs_hash = builder.record_entry_contents(
2776.4.13 by Robert Collins
Merge bzr.dev.
226
                ie, [parent_tree.inventory], '', tree,
2871.1.4 by Robert Collins
Merge bzr.dev.
227
                tree.path_content_summary(''))
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
228
            # Regardless of repository root behaviour we should consider this a
229
            # pointless commit.
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
230
            self.assertFalse(builder.any_changes())
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
231
            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
232
            # if the repository format recorded a new root revision, that
233
            # should be in the delta
234
            got_new_revision = ie.revision != old_revision_id
235
            if got_new_revision:
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
236
                self.assertEqual(('', '', ie.file_id, ie), delta)
237
                # The delta should be tracked
238
                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
239
            else:
240
                self.assertEqual(None, delta)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
241
            # Directories do not get hashed.
242
            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
243
            builder.abort()
244
        except:
245
            builder.abort()
246
            tree.unlock()
247
            raise
248
        else:
249
            tree.unlock()
1910.2.8 by Aaron Bentley
Fix commit_builder when root not passed to record_entry_contents
250
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
251
    def test_commit_unchanged_root_record_iter_changes(self):
252
        tree = self.make_branch_and_tree(".")
253
        old_revision_id = tree.commit('')
254
        tree.lock_write()
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
255
        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.
256
        try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
257
            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.
258
            # Regardless of repository root behaviour we should consider this a
259
            # pointless commit.
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
260
            self.assertFalse(builder.any_changes())
3775.2.7 by Robert Collins
CommitBuilder handles no-change commits to roots properly with record_iter_changes.
261
            builder.finish_inventory()
262
            new_root = tree.branch.repository.get_inventory(
263
                builder._new_revision_id).root
264
            if tree.branch.repository.supports_rich_root():
265
                # We should not have seen a new root revision
266
                self.assertEqual(old_revision_id, new_root.revision)
267
            else:
268
                # We should see a new root revision
269
                self.assertNotEqual(old_revision_id, new_root.revision)
270
        finally:
271
            builder.abort()
272
            tree.unlock()
273
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
274
    def test_commit(self):
1740.3.8 by Jelmer Vernooij
Move make_revision() to commit builder.
275
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
276
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
277
        try:
278
            builder = tree.branch.get_commit_builder([])
279
            self.record_root(builder, tree)
280
            builder.finish_inventory()
281
            rev_id = builder.commit('foo bar')
282
        finally:
283
            tree.unlock()
1740.3.9 by Jelmer Vernooij
Make the commit message the first argument of CommitBuilder.commit().
284
        self.assertNotEqual(None, rev_id)
285
        self.assertTrue(tree.branch.repository.has_revision(rev_id))
1757.1.2 by Robert Collins
Bugfix CommitBuilders recording of the inventory revision id.
286
        # the revision id must be set on the inventory when saving it. This does not
287
        # precisely test that - a repository that wants to can add it on deserialisation,
288
        # but thats all the current contract guarantees anyway.
289
        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
290
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
291
    def test_get_basis_delta(self):
292
        tree = self.make_branch_and_tree(".")
293
        self.build_tree(["foo"])
294
        tree.add(["foo"], ["foo-id"])
295
        old_revision_id = tree.commit("added foo")
296
        tree.lock_write()
297
        try:
298
            self.build_tree(['bar'])
299
            tree.add(['bar'], ['bar-id'])
300
            basis = tree.branch.repository.revision_tree(old_revision_id)
301
            basis.lock_read()
302
            self.addCleanup(basis.unlock)
303
            builder = tree.branch.get_commit_builder([old_revision_id])
304
            total_delta = []
305
            try:
306
                parent_invs = [basis.inventory]
307
                builder.will_record_deletes()
308
                if builder.record_root_entry:
309
                    ie = basis.inventory.root.copy()
310
                    delta, _, _ = builder.record_entry_contents(ie, parent_invs,
311
                        '', tree, tree.path_content_summary(''))
312
                    if delta is not None:
313
                        total_delta.append(delta)
314
                delta = builder.record_delete("foo", "foo-id")
315
                total_delta.append(delta)
316
                new_bar = inventory.make_entry('file', 'bar',
317
                    parent_id=tree.get_root_id(), file_id='bar-id')
318
                delta, _, _ = builder.record_entry_contents(new_bar, parent_invs,
319
                    'bar', tree, tree.path_content_summary('bar'))
320
                total_delta.append(delta)
321
                # All actions should have been recorded in the basis_delta
322
                self.assertEqual(total_delta, builder.get_basis_delta())
323
                builder.finish_inventory()
324
                builder.commit('delete foo, add bar')
325
            except:
326
                tree.branch.repository.abort_write_group()
327
                raise
328
        finally:
329
            tree.unlock()
330
331
    def test_get_basis_delta_without_notification(self):
332
        tree = self.make_branch_and_tree(".")
333
        old_revision_id = tree.commit('')
334
        tree.lock_write()
335
        try:
336
            parent_tree = tree.basis_tree()
337
            parent_tree.lock_read()
338
            self.addCleanup(parent_tree.unlock)
339
            builder = tree.branch.get_commit_builder([old_revision_id])
340
            # It is an error to expect builder.get_basis_delta() to be correct,
341
            # if you have not also called will_record_deletes() to indicate you
342
            # will be calling record_delete() when appropriate
343
            self.assertRaises(AssertionError, builder.get_basis_delta)
344
            tree.branch.repository.abort_write_group()
345
        finally:
346
            tree.unlock()
347
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
348
    def test_record_delete(self):
349
        tree = self.make_branch_and_tree(".")
350
        self.build_tree(["foo"])
351
        tree.add(["foo"], ["foo-id"])
352
        rev_id = tree.commit("added foo")
353
        # Remove the inventory details for foo-id, because
354
        # record_entry_contents ends up copying root verbatim.
355
        tree.unversion(["foo-id"])
356
        tree.lock_write()
357
        try:
358
            basis = tree.branch.repository.revision_tree(rev_id)
359
            builder = tree.branch.get_commit_builder([rev_id])
360
            try:
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
361
                builder.will_record_deletes()
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
362
                if builder.record_root_entry is True:
363
                    parent_invs = [basis.inventory]
364
                    del basis.inventory.root.children['foo']
365
                    builder.record_entry_contents(basis.inventory.root,
366
                        parent_invs, '', tree, tree.path_content_summary(''))
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
367
                # the delta should be returned, and recorded in _basis_delta
368
                delta = builder.record_delete("foo", "foo-id")
369
                self.assertEqual(("foo", None, "foo-id", None), delta)
370
                self.assertEqual(delta, builder._basis_delta[-1])
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
371
                builder.finish_inventory()
372
                rev_id2 = builder.commit('delete foo')
373
            except:
374
                tree.branch.repository.abort_write_group()
375
                raise
376
        finally:
377
            tree.unlock()
378
        rev_tree = builder.revision_tree()
379
        rev_tree.lock_read()
380
        self.addCleanup(rev_tree.unlock)
381
        self.assertFalse(rev_tree.path2id('foo'))
382
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
383
    def test_record_delete_record_iter_changes(self):
384
        tree = self.make_branch_and_tree(".")
385
        self.build_tree(["foo"])
386
        tree.add(["foo"], ["foo-id"])
387
        rev_id = tree.commit("added foo")
388
        tree.lock_write()
389
        try:
390
            builder = tree.branch.get_commit_builder([rev_id])
391
            try:
392
                delete_change = ('foo-id', ('foo', None), True, (True, False),
393
                    (tree.path2id(''), None), ('foo', None), ('file', None),
394
                    (False, None))
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
395
                list(builder.record_iter_changes(tree, rev_id,
396
                    [delete_change]))
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
397
                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.
398
                    builder._basis_delta[0])
3775.2.9 by Robert Collins
CommitBuilder handles deletes via record_iter_entries.
399
                self.assertTrue(builder.any_changes())
400
                builder.finish_inventory()
401
                rev_id2 = builder.commit('delete foo')
402
            except:
403
                builder.abort()
404
                raise
405
        finally:
406
            tree.unlock()
407
        rev_tree = builder.revision_tree()
408
        rev_tree.lock_read()
409
        self.addCleanup(rev_tree.unlock)
410
        self.assertFalse(rev_tree.path2id('foo'))
411
3775.2.2 by Robert Collins
Teach CommitBuilder to accumulate inventory deltas.
412
    def test_record_delete_without_notification(self):
413
        tree = self.make_branch_and_tree(".")
414
        self.build_tree(["foo"])
415
        tree.add(["foo"], ["foo-id"])
416
        rev_id = tree.commit("added foo")
417
        tree.lock_write()
418
        try:
419
            builder = tree.branch.get_commit_builder([rev_id])
420
            try:
421
                self.record_root(builder, tree)
422
                self.assertRaises(AssertionError,
423
                    builder.record_delete, "foo", "foo-id")
424
            finally:
425
                tree.branch.repository.abort_write_group()
426
        finally:
427
            tree.unlock()
428
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
429
    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
430
        tree = self.make_branch_and_tree(".")
2617.6.2 by Robert Collins
Add abort_write_group and wire write_groups into fetch and commit.
431
        tree.lock_write()
2617.6.8 by Robert Collins
Review feedback and documentation.
432
        try:
433
            builder = tree.branch.get_commit_builder([])
434
            self.record_root(builder, tree)
435
            builder.finish_inventory()
436
            rev_id = builder.commit('foo bar')
437
        finally:
438
            tree.unlock()
2041.1.5 by John Arbash Meinel
CommitBuilder.get_tree => CommitBuilder.revision_tree
439
        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
440
        # Just a couple simple tests to ensure that it actually follows
441
        # the RevisionTree api.
442
        self.assertEqual(rev_id, rev_tree.get_revision_id())
443
        self.assertEqual([], rev_tree.get_parent_ids())
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
444
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
445
    def test_revision_tree_record_iter_changes(self):
446
        tree = self.make_branch_and_tree(".")
447
        tree.lock_write()
448
        try:
449
            builder = tree.branch.get_commit_builder([])
450
            try:
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
451
                list(builder.record_iter_changes(tree,
452
                    _mod_revision.NULL_REVISION,
453
                    tree.iter_changes(tree.basis_tree())))
3775.2.10 by Robert Collins
CommitBuilder gives a revision tree when used with record_iter_contents.
454
                builder.finish_inventory()
455
                rev_id = builder.commit('foo bar')
456
            except:
457
                builder.abort()
458
                raise
459
            rev_tree = builder.revision_tree()
460
            # Just a couple simple tests to ensure that it actually follows
461
            # the RevisionTree api.
462
            self.assertEqual(rev_id, rev_tree.get_revision_id())
463
            self.assertEqual([], rev_tree.get_parent_ids())
464
        finally:
465
            tree.unlock()
466
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
467
    def test_root_entry_has_revision(self):
468
        # test the root revision created and put in the basis
469
        # 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.
470
        # XXX: RBC 20081118 - this test is too big, it depends on the exact
471
        # behaviour of tree methods and so on; it should be written to the
472
        # commit builder interface directly.
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
473
        tree = self.make_branch_and_tree('.')
474
        rev_id = tree.commit('message')
475
        basis_tree = tree.basis_tree()
476
        basis_tree.lock_read()
477
        self.addCleanup(basis_tree.unlock)
478
        self.assertEqual(rev_id, basis_tree.inventory.root.revision)
479
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
480
    def _get_revtrees(self, tree, revision_ids):
2592.3.214 by Robert Collins
Merge bzr.dev.
481
        tree.lock_read()
482
        try:
483
            trees = list(tree.branch.repository.revision_trees(revision_ids))
484
            for _tree in trees:
485
                _tree.lock_read()
486
                self.addCleanup(_tree.unlock)
487
            return trees
488
        finally:
489
            tree.unlock()
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
490
491
    def test_last_modified_revision_after_commit_root_unchanged(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
492
        # 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.
493
        # last modified except on non-rich-root-repositories.
494
        tree = self.make_branch_and_tree('.')
495
        rev1 = tree.commit('')
496
        rev2 = tree.commit('')
497
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
498
        self.assertEqual(rev1, tree1.inventory.root.revision)
499
        if tree.branch.repository.supports_rich_root():
500
            self.assertEqual(rev1, tree2.inventory.root.revision)
501
        else:
502
            self.assertEqual(rev2, tree2.inventory.root.revision)
503
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
504
    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.
505
        tree.add([name], [name + 'id'])
4183.5.3 by Robert Collins
Fix typo.
506
        self._commit_check_unchanged(tree, name, name + 'id',
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
507
            mini_commit=mini_commit)
508
509
    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.
510
        rev1 = tree.commit('')
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
511
        if mini_commit is None:
512
            mini_commit = self.mini_commit
513
        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.
514
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
515
        self.assertEqual(rev1, tree1.inventory[file_id].revision)
516
        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.
517
        expected_graph = {}
518
        expected_graph[(file_id, rev1)] = ()
519
        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.
520
521
    def test_last_modified_revision_after_commit_dir_unchanged(self):
522
        # committing without changing a dir does not change the last modified.
523
        tree = self.make_branch_and_tree('.')
524
        self.build_tree(['dir/'])
525
        self._add_commit_check_unchanged(tree, 'dir')
526
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
527
    def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
528
        # committing without changing a dir does not change the last modified.
529
        tree = self.make_branch_and_tree('.')
530
        self.build_tree(['dir/'])
531
        self._add_commit_check_unchanged(tree, 'dir',
532
            mini_commit=self.mini_commit_record_iter_changes)
533
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
534
    def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
535
        # committing without changing a dir does not change the last modified
536
        # of the dir even the dirs contents are changed.
537
        tree = self.make_branch_and_tree('.')
538
        self.build_tree(['dir/'])
539
        tree.add(['dir'], ['dirid'])
540
        rev1 = tree.commit('')
541
        self.build_tree(['dir/content'])
542
        tree.add(['dir/content'], ['contentid'])
543
        rev2 = tree.commit('')
544
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
545
        self.assertEqual(rev1, tree1.inventory['dirid'].revision)
546
        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.
547
        file_id = 'dirid'
548
        expected_graph = {}
549
        expected_graph[(file_id, rev1)] = ()
550
        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.
551
552
    def test_last_modified_revision_after_commit_file_unchanged(self):
553
        # committing without changing a file does not change the last modified.
554
        tree = self.make_branch_and_tree('.')
555
        self.build_tree(['file'])
556
        self._add_commit_check_unchanged(tree, 'file')
557
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
558
    def test_last_modified_revision_after_commit_file_unchanged_ric(self):
559
        # committing without changing a file does not change the last modified.
560
        tree = self.make_branch_and_tree('.')
561
        self.build_tree(['file'])
562
        self._add_commit_check_unchanged(tree, 'file',
563
            mini_commit=self.mini_commit_record_iter_changes)
564
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
565
    def test_last_modified_revision_after_commit_link_unchanged(self):
566
        # committing without changing a link does not change the last modified.
567
        self.requireFeature(tests.SymlinkFeature)
568
        tree = self.make_branch_and_tree('.')
569
        os.symlink('target', 'link')
570
        self._add_commit_check_unchanged(tree, 'link')
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_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.
573
        # committing without changing a link does not change the last modified.
574
        self.requireFeature(tests.SymlinkFeature)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
575
        tree = self.make_branch_and_tree('.')
576
        os.symlink('target', 'link')
577
        self._add_commit_check_unchanged(tree, 'link',
578
            mini_commit=self.mini_commit_record_iter_changes)
579
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
580
    def test_last_modified_revision_after_commit_reference_unchanged(self):
581
        # committing without changing a subtree does not change the last
582
        # modified.
583
        tree = self.make_branch_and_tree('.')
584
        subtree = self.make_reference('reference')
585
        try:
586
            tree.add_reference(subtree)
587
            self._commit_check_unchanged(tree, 'reference',
588
                subtree.get_root_id())
589
        except errors.UnsupportedOperation:
590
            return
591
592
    def test_last_modified_revision_after_commit_reference_unchanged_ric(self):
593
        # committing without changing a subtree does not change the last
594
        # modified.
595
        tree = self.make_branch_and_tree('.')
596
        subtree = self.make_reference('reference')
597
        try:
598
            tree.add_reference(subtree)
599
            self._commit_check_unchanged(tree, 'reference',
600
                subtree.get_root_id(),
601
                mini_commit=self.mini_commit_record_iter_changes)
602
        except errors.UnsupportedOperation:
603
            return
604
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
605
    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.
606
        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.
607
        def rename():
608
            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.
609
        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.
610
            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.
611
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
612
    def _commit_renamed_check_changed(self, tree, name, file_id,
613
        expect_fs_hash=False, mini_commit=None):
614
        def rename():
615
            tree.rename_one(name, 'new_' + name)
616
        self._commit_change_check_changed(tree, name, file_id, rename,
617
            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
618
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
619
    def test_last_modified_revision_after_rename_dir_changes(self):
620
        # renaming a dir changes the last modified.
621
        tree = self.make_branch_and_tree('.')
622
        self.build_tree(['dir/'])
623
        self._add_commit_renamed_check_changed(tree, 'dir')
624
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
625
    def test_last_modified_revision_after_rename_dir_changes_ric(self):
626
        # renaming a dir changes the last modified.
627
        tree = self.make_branch_and_tree('.')
628
        self.build_tree(['dir/'])
629
        self._add_commit_renamed_check_changed(tree, 'dir',
630
            mini_commit=self.mini_commit_record_iter_changes)
631
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
632
    def test_last_modified_revision_after_rename_file_changes(self):
633
        # renaming a file changes the last modified.
634
        tree = self.make_branch_and_tree('.')
635
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
636
        self._add_commit_renamed_check_changed(tree, 'file',
637
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
638
3775.2.12 by Robert Collins
CommitBuilder.record_iter_changes handles renamed files.
639
    def test_last_modified_revision_after_rename_file_changes_ric(self):
640
        # renaming a file changes the last modified.
641
        tree = self.make_branch_and_tree('.')
642
        self.build_tree(['file'])
643
        self._add_commit_renamed_check_changed(tree, 'file',
644
            expect_fs_hash=True,
645
            mini_commit=self.mini_commit_record_iter_changes)
646
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
647
    def test_last_modified_revision_after_rename_link_changes(self):
648
        # renaming a link changes the last modified.
649
        self.requireFeature(tests.SymlinkFeature)
650
        tree = self.make_branch_and_tree('.')
651
        os.symlink('target', 'link')
652
        self._add_commit_renamed_check_changed(tree, 'link')
653
3775.2.13 by Robert Collins
CommitBuilder.record_iter_changes handles renamed symlinks.
654
    def test_last_modified_revision_after_rename_link_changes_ric(self):
655
        # renaming a link changes the last modified.
656
        self.requireFeature(tests.SymlinkFeature)
657
        tree = self.make_branch_and_tree('.')
658
        os.symlink('target', 'link')
659
        self._add_commit_renamed_check_changed(tree, 'link',
660
            mini_commit=self.mini_commit_record_iter_changes)
661
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
662
    def test_last_modified_revision_after_rename_ref_changes(self):
663
        # renaming a reference changes the last modified.
664
        tree = self.make_branch_and_tree('.')
665
        subtree = self.make_reference('reference')
666
        try:
667
            tree.add_reference(subtree)
668
            self._commit_renamed_check_changed(tree, 'reference',
669
                subtree.get_root_id())
670
        except errors.UnsupportedOperation:
671
            return
672
673
    def test_last_modified_revision_after_rename_ref_changes_ric(self):
674
        # renaming a reference changes the last modified.
675
        tree = self.make_branch_and_tree('.')
676
        subtree = self.make_reference('reference')
677
        try:
678
            tree.add_reference(subtree)
679
            self._commit_renamed_check_changed(tree, 'reference',
680
                subtree.get_root_id(),
681
                mini_commit=self.mini_commit_record_iter_changes)
682
        except errors.UnsupportedOperation:
683
            return
684
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
685
    def _add_commit_reparent_check_changed(self, tree, name,
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
686
        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.
687
        self.build_tree(['newparent/'])
688
        tree.add(['newparent'])
689
        def reparent():
690
            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.
691
        self._add_commit_change_check_changed(tree, name, reparent,
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
692
            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.
693
694
    def test_last_modified_revision_after_reparent_dir_changes(self):
695
        # reparenting a dir changes the last modified.
696
        tree = self.make_branch_and_tree('.')
697
        self.build_tree(['dir/'])
698
        self._add_commit_reparent_check_changed(tree, 'dir')
699
3775.2.14 by Robert Collins
CommitBuilder.record_iter_changes handles reparented directories.
700
    def test_last_modified_revision_after_reparent_dir_changes_ric(self):
701
        # reparenting a dir changes the last modified.
702
        tree = self.make_branch_and_tree('.')
703
        self.build_tree(['dir/'])
704
        self._add_commit_reparent_check_changed(tree, 'dir',
705
            mini_commit=self.mini_commit_record_iter_changes)
706
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
707
    def test_last_modified_revision_after_reparent_file_changes(self):
708
        # reparenting a file changes the last modified.
709
        tree = self.make_branch_and_tree('.')
710
        self.build_tree(['file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
711
        self._add_commit_reparent_check_changed(tree, 'file',
712
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
713
3775.2.15 by Robert Collins
CommitBuilder.record_iter_changes handles reparented files.
714
    def test_last_modified_revision_after_reparent_file_changes_ric(self):
715
        # reparenting a file changes the last modified.
716
        tree = self.make_branch_and_tree('.')
717
        self.build_tree(['file'])
718
        self._add_commit_reparent_check_changed(tree, 'file',
3775.2.17 by Robert Collins
CommitBuilder.record_iter_changes handles changed files.
719
            expect_fs_hash=True,
720
            mini_commit=self.mini_commit_record_iter_changes)
3775.2.15 by Robert Collins
CommitBuilder.record_iter_changes handles reparented files.
721
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
722
    def test_last_modified_revision_after_reparent_link_changes(self):
723
        # reparenting a link changes the last modified.
724
        self.requireFeature(tests.SymlinkFeature)
725
        tree = self.make_branch_and_tree('.')
726
        os.symlink('target', 'link')
727
        self._add_commit_reparent_check_changed(tree, 'link')
728
3775.2.16 by Robert Collins
CommitBuilder.record_iter_changes handles reparented symlinks.
729
    def test_last_modified_revision_after_reparent_link_changes_ric(self):
730
        # reparenting a link changes the last modified.
731
        self.requireFeature(tests.SymlinkFeature)
732
        tree = self.make_branch_and_tree('.')
733
        os.symlink('target', 'link')
734
        self._add_commit_reparent_check_changed(tree, 'link',
735
            mini_commit=self.mini_commit_record_iter_changes)
736
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
737
    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.
738
        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.
739
        tree.add([name], [name + 'id'])
4183.5.3 by Robert Collins
Fix typo.
740
        self._commit_change_check_changed(tree, name, name + 'id',
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
741
            changer, expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
742
4183.5.3 by Robert Collins
Fix typo.
743
    def _commit_change_check_changed(self, tree, name, file_id, changer,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
744
        expect_fs_hash=False, mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
745
        rev1 = tree.commit('')
746
        changer()
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
747
        if mini_commit is None:
748
            mini_commit = self.mini_commit
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
749
        rev2 = mini_commit(tree, name, tree.id2path(file_id),
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
750
            expect_fs_hash=expect_fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
751
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
752
        self.assertEqual(rev1, tree1.inventory[file_id].revision)
753
        self.assertEqual(rev2, tree2.inventory[file_id].revision)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
754
        expected_graph = {}
755
        expected_graph[(file_id, rev1)] = ()
756
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
757
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
758
759
    def mini_commit(self, tree, name, new_name, records_version=True,
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
760
        delta_against_basis=True, expect_fs_hash=False):
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
761
        """Perform a miniature commit looking for record entry results.
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
762
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
763
        :param tree: The tree to commit.
764
        :param name: The path in the basis tree of the tree being committed.
765
        :param new_name: The path in the tree being committed.
766
        :param records_version: True if the commit of new_name is expected to
767
            record a new version.
768
        :param delta_against_basis: True of the commit of new_name is expected
769
            to have a delta against the basis.
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
770
        :param expect_fs_hash: True or false to indicate whether we expect a
771
            file hash to be returned from the record_entry_contents call.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
772
        """
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
773
        tree.lock_write()
774
        try:
775
            # mini manual commit here so we can check the return of
776
            # record_entry_contents.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
777
            parent_ids = tree.get_parent_ids()
778
            builder = tree.branch.get_commit_builder(parent_ids)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
779
            parent_tree = tree.basis_tree()
780
            parent_tree.lock_read()
781
            self.addCleanup(parent_tree.unlock)
782
            parent_invs = [parent_tree.inventory]
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
783
            for parent_id in parent_ids[1:]:
784
                parent_invs.append(tree.branch.repository.revision_tree(
785
                    parent_id).inventory)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
786
            # root
787
            builder.record_entry_contents(
788
                inventory.make_entry('directory', '', None,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
789
                    tree.get_root_id()), parent_invs, '', tree,
2776.4.13 by Robert Collins
Merge bzr.dev.
790
                    tree.path_content_summary(''))
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
791
            def commit_id(file_id):
792
                old_ie = tree.inventory[file_id]
793
                path = tree.id2path(file_id)
794
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
795
                    old_ie.parent_id, file_id)
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
796
                content_summary = tree.path_content_summary(path)
797
                if content_summary[0] == 'tree-reference':
798
                    content_summary = content_summary[:3] + (
799
                        tree.get_reference_revision(file_id),)
2776.4.13 by Robert Collins
Merge bzr.dev.
800
                return builder.record_entry_contents(ie, parent_invs, path,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
801
                    tree, content_summary)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
802
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
803
            file_id = tree.path2id(new_name)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
804
            parent_id = tree.inventory[file_id].parent_id
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
805
            if parent_id != tree.get_root_id():
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
806
                commit_id(parent_id)
807
            # because a change of some sort is meant to have occurred,
808
            # recording the entry must return True.
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
809
            delta, version_recorded, fs_hash = commit_id(file_id)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
810
            if records_version:
811
                self.assertTrue(version_recorded)
812
            else:
813
                self.assertFalse(version_recorded)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
814
            if expect_fs_hash:
3709.3.2 by Robert Collins
Race-free stat-fingerprint updating during commit via a new method get_file_with_stat.
815
                tree_file_stat = tree.get_file_with_stat(file_id)
816
                tree_file_stat[0].close()
817
                self.assertEqual(2, len(fs_hash))
818
                self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
819
                self.assertEqualStat(tree_file_stat[1], fs_hash[1])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
820
            else:
821
                self.assertEqual(None, fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
822
            new_entry = builder.new_inventory[file_id]
823
            if delta_against_basis:
824
                expected_delta = (name, new_name, file_id, new_entry)
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
825
                # The delta should be recorded
826
                self.assertEqual(expected_delta, builder._basis_delta[-1])
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
827
            else:
828
                expected_delta = None
829
            self.assertEqual(expected_delta, delta)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
830
            builder.finish_inventory()
831
            rev2 = builder.commit('')
832
        except:
833
            builder.abort()
834
            tree.unlock()
835
            raise
4245.1.1 by Ian Clatworthy
minor test clean-ups & _reconcile_pack API
836
        try:
837
            tree.set_parent_ids([rev2])
838
        finally:
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
839
            tree.unlock()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
840
        return rev2
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
841
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
842
    def mini_commit_record_iter_changes(self, tree, name, new_name,
843
        records_version=True, delta_against_basis=True, expect_fs_hash=False):
844
        """Perform a miniature commit looking for record entry results.
845
846
        This version uses the record_iter_changes interface.
847
        
848
        :param tree: The tree to commit.
849
        :param name: The path in the basis tree of the tree being committed.
850
        :param new_name: The path in the tree being committed.
851
        :param records_version: True if the commit of new_name is expected to
852
            record a new version.
853
        :param delta_against_basis: True of the commit of new_name is expected
854
            to have a delta against the basis.
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
855
        :param expect_fs_hash: If true, looks for a fs hash output from
856
            record_iter_changes.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
857
        """
858
        tree.lock_write()
859
        try:
860
            # mini manual commit here so we can check the return of
861
            # record_entry_contents.
862
            parent_ids = tree.get_parent_ids()
863
            builder = tree.branch.get_commit_builder(parent_ids)
864
            parent_tree = tree.basis_tree()
865
            parent_tree.lock_read()
866
            self.addCleanup(parent_tree.unlock)
867
            parent_invs = [parent_tree.inventory]
868
            for parent_id in parent_ids[1:]:
869
                parent_invs.append(tree.branch.repository.revision_tree(
870
                    parent_id).inventory)
871
            changes = list(tree.iter_changes(parent_tree))
4183.5.4 by Robert Collins
Turn record_iter_changes into a generator to emit file system hashes.
872
            result = list(builder.record_iter_changes(tree, parent_ids[0],
873
                changes))
874
            file_id = tree.path2id(new_name)
875
            if expect_fs_hash:
876
                tree_file_stat = tree.get_file_with_stat(file_id)
877
                tree_file_stat[0].close()
878
                self.assertLength(1, result)
879
                result = result[0]
880
                self.assertEqual(result[:2], (file_id, new_name))
881
                self.assertEqual(result[2][0], tree.get_file_sha1(file_id))
882
                self.assertEqualStat(result[2][1], tree_file_stat[1])
883
            else:
884
                self.assertEqual([], result)
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
885
            delta = builder._basis_delta
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
886
            delta_dict = dict((change[2], change) for change in delta)
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
887
            version_recorded = (file_id in delta_dict and
888
                delta_dict[file_id][3] is not None and
889
                delta_dict[file_id][3].revision == builder._new_revision_id)
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
890
            if records_version:
891
                self.assertTrue(version_recorded)
892
            else:
893
                self.assertFalse(version_recorded)
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')