/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,
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
738
        expect_fs_hash=False, mini_commit=None, file_id=None):
739
        if file_id is None:
740
            file_id = name + 'id'
741
        tree.add([name], [file_id])
742
        self._commit_change_check_changed(
743
            tree, name, file_id,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
744
            changer, expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
745
4183.5.3 by Robert Collins
Fix typo.
746
    def _commit_change_check_changed(self, tree, name, file_id, changer,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
747
        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.
748
        rev1 = tree.commit('')
749
        changer()
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
750
        if mini_commit is None:
751
            mini_commit = self.mini_commit
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
752
        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.
753
            expect_fs_hash=expect_fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
754
        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
755
        self.assertEqual(rev1, tree1.inventory[file_id].revision)
756
        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.
757
        expected_graph = {}
758
        expected_graph[(file_id, rev1)] = ()
759
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
760
        self.assertFileGraph(expected_graph, tree, (file_id, rev2))
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
761
762
    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.
763
        delta_against_basis=True, expect_fs_hash=False):
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
764
        """Perform a miniature commit looking for record entry results.
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
765
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
766
        :param tree: The tree to commit.
767
        :param name: The path in the basis tree of the tree being committed.
768
        :param new_name: The path in the tree being committed.
769
        :param records_version: True if the commit of new_name is expected to
770
            record a new version.
771
        :param delta_against_basis: True of the commit of new_name is expected
772
            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.
773
        :param expect_fs_hash: True or false to indicate whether we expect a
774
            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
775
        """
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
776
        tree.lock_write()
777
        try:
778
            # mini manual commit here so we can check the return of
779
            # record_entry_contents.
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
780
            parent_ids = tree.get_parent_ids()
781
            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
782
            parent_tree = tree.basis_tree()
783
            parent_tree.lock_read()
784
            self.addCleanup(parent_tree.unlock)
785
            parent_invs = [parent_tree.inventory]
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
786
            for parent_id in parent_ids[1:]:
787
                parent_invs.append(tree.branch.repository.revision_tree(
788
                    parent_id).inventory)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
789
            # root
790
            builder.record_entry_contents(
791
                inventory.make_entry('directory', '', None,
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
792
                    tree.get_root_id()), parent_invs, '', tree,
2776.4.13 by Robert Collins
Merge bzr.dev.
793
                    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
794
            def commit_id(file_id):
795
                old_ie = tree.inventory[file_id]
796
                path = tree.id2path(file_id)
797
                ie = inventory.make_entry(tree.kind(file_id), old_ie.name,
798
                    old_ie.parent_id, file_id)
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
799
                content_summary = tree.path_content_summary(path)
800
                if content_summary[0] == 'tree-reference':
801
                    content_summary = content_summary[:3] + (
802
                        tree.get_reference_revision(file_id),)
2776.4.13 by Robert Collins
Merge bzr.dev.
803
                return builder.record_entry_contents(ie, parent_invs, path,
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
804
                    tree, content_summary)
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
805
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
806
            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
807
            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('')
808
            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
809
                commit_id(parent_id)
810
            # because a change of some sort is meant to have occurred,
811
            # 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.
812
            delta, version_recorded, fs_hash = commit_id(file_id)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
813
            if records_version:
814
                self.assertTrue(version_recorded)
815
            else:
816
                self.assertFalse(version_recorded)
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
817
            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.
818
                tree_file_stat = tree.get_file_with_stat(file_id)
819
                tree_file_stat[0].close()
820
                self.assertEqual(2, len(fs_hash))
821
                self.assertEqual(tree.get_file_sha1(file_id), fs_hash[0])
822
                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.
823
            else:
824
                self.assertEqual(None, fs_hash)
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
825
            new_entry = builder.new_inventory[file_id]
826
            if delta_against_basis:
827
                expected_delta = (name, new_name, file_id, new_entry)
3879.2.5 by John Arbash Meinel
Change record_delete() to return the delta.
828
                # The delta should be recorded
829
                self.assertEqual(expected_delta, builder._basis_delta[-1])
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
830
            else:
831
                expected_delta = None
832
            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
833
            builder.finish_inventory()
834
            rev2 = builder.commit('')
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
835
        except:
836
            builder.abort()
837
            tree.unlock()
838
            raise
4245.1.1 by Ian Clatworthy
minor test clean-ups & _reconcile_pack API
839
        try:
840
            tree.set_parent_ids([rev2])
841
        finally:
2825.5.1 by Robert Collins
* Committing a change which is not a merge and does not change the number of
842
            tree.unlock()
2871.1.3 by Robert Collins
* The CommitBuilder method ``record_entry_contents`` now returns summary
843
        return rev2
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
844
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
845
    def mini_commit_record_iter_changes(self, tree, name, new_name,
846
        records_version=True, delta_against_basis=True, expect_fs_hash=False):
847
        """Perform a miniature commit looking for record entry results.
848
849
        This version uses the record_iter_changes interface.
850
        
851
        :param tree: The tree to commit.
852
        :param name: The path in the basis tree of the tree being committed.
853
        :param new_name: The path in the tree being committed.
854
        :param records_version: True if the commit of new_name is expected to
855
            record a new version.
856
        :param delta_against_basis: True of the commit of new_name is expected
857
            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.
858
        :param expect_fs_hash: If true, looks for a fs hash output from
859
            record_iter_changes.
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
860
        """
861
        tree.lock_write()
862
        try:
863
            # mini manual commit here so we can check the return of
864
            # record_entry_contents.
865
            parent_ids = tree.get_parent_ids()
866
            builder = tree.branch.get_commit_builder(parent_ids)
867
            parent_tree = tree.basis_tree()
868
            parent_tree.lock_read()
869
            self.addCleanup(parent_tree.unlock)
870
            parent_invs = [parent_tree.inventory]
871
            for parent_id in parent_ids[1:]:
872
                parent_invs.append(tree.branch.repository.revision_tree(
873
                    parent_id).inventory)
874
            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.
875
            result = list(builder.record_iter_changes(tree, parent_ids[0],
876
                changes))
877
            file_id = tree.path2id(new_name)
878
            if expect_fs_hash:
879
                tree_file_stat = tree.get_file_with_stat(file_id)
880
                tree_file_stat[0].close()
881
                self.assertLength(1, result)
882
                result = result[0]
883
                self.assertEqual(result[:2], (file_id, new_name))
884
                self.assertEqual(result[2][0], tree.get_file_sha1(file_id))
885
                self.assertEqualStat(result[2][1], tree_file_stat[1])
886
            else:
887
                self.assertEqual([], result)
3775.2.29 by Robert Collins
Updates to the form of add_inventory_by_delta that landed in trunk.
888
            delta = builder._basis_delta
3775.2.11 by Robert Collins
CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
889
            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.
890
            version_recorded = (file_id in delta_dict and
891
                delta_dict[file_id][3] is not None and
892
                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.
893
            if records_version:
894
                self.assertTrue(version_recorded)
895
            else:
896
                self.assertFalse(version_recorded)
897
            builder.finish_inventory()
898
            new_inventory = builder.revision_tree().inventory
899
            new_entry = new_inventory[file_id]
900
            if delta_against_basis:
901
                expected_delta = (name, new_name, file_id, new_entry)
902
                self.assertEqual(expected_delta, delta_dict[file_id])
903
            else:
904
                expected_delta = None
905
                self.assertFalse(version_recorded)
906
            rev2 = builder.commit('')
907
            tree.set_parent_ids([rev2])
908
        except:
909
            builder.abort()
910
            tree.unlock()
911
            raise
912
        else:
913
            tree.unlock()
914
        return rev2
915
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.
916
    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.
917
        # all the changes that have occured should be in the ancestry
918
        # (closest to a public per-file graph API we have today)
919
        tree.lock_read()
920
        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.
921
        graph = dict(Graph(tree.branch.repository.texts).iter_ancestry([tip]))
922
        self.assertEqual(expected_graph, graph)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
923
924
    def test_last_modified_revision_after_content_file_changes(self):
925
        # altering a file changes the last modified.
926
        tree = self.make_branch_and_tree('.')
927
        self.build_tree(['file'])
928
        def change_file():
929
            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.
930
        self._add_commit_change_check_changed(tree, 'file', change_file,
931
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
932
3775.2.17 by Robert Collins
CommitBuilder.record_iter_changes handles changed files.
933
    def test_last_modified_revision_after_content_file_changes_ric(self):
934
        # altering a file changes the last modified.
935
        tree = self.make_branch_and_tree('.')
936
        self.build_tree(['file'])
937
        def change_file():
938
            tree.put_file_bytes_non_atomic('fileid', 'new content')
939
        self._add_commit_change_check_changed(tree, 'file', change_file,
940
            expect_fs_hash=True,
941
            mini_commit=self.mini_commit_record_iter_changes)
942
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
943
    def test_last_modified_revision_after_content_link_changes(self):
944
        # changing a link changes the last modified.
945
        self.requireFeature(tests.SymlinkFeature)
946
        tree = self.make_branch_and_tree('.')
947
        os.symlink('target', 'link')
948
        def change_link():
949
            os.unlink('link')
950
            os.symlink('newtarget', 'link')
951
        self._add_commit_change_check_changed(tree, 'link', change_link)
952
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
953
    def _test_last_mod_rev_after_content_link_changes_ric(
954
        self, link, target, newtarget, file_id=None):
955
        if file_id is None:
956
            file_id = link
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
957
        # changing a link changes the last modified.
958
        self.requireFeature(tests.SymlinkFeature)
959
        tree = self.make_branch_and_tree('.')
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
960
        os.symlink(target, link)
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
961
        def change_link():
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
962
            os.unlink(link)
963
            os.symlink(newtarget, link)
964
        self._add_commit_change_check_changed(
965
            tree, link, change_link,
966
            mini_commit=self.mini_commit_record_iter_changes,
967
            file_id=file_id)
968
969
    def test_last_modified_rev_after_content_link_changes_ric(self):
970
        self._test_last_mod_rev_after_content_link_changes_ric(
971
            'link', 'target', 'newtarget')
972
973
    def test_last_modified_rev_after_content_unicode_link_changes_ric(self):
974
        self.requireFeature(tests.UnicodeFilenameFeature)
975
        self._test_last_mod_rev_after_content_link_changes_ric(
976
            u'li\u1234nk', u'targ\N{Euro Sign}t', u'n\N{Euro Sign}wtarget',
977
978
            file_id=u'li\u1234nk'.encode('UTF-8'))
3775.2.18 by Robert Collins
CommitBuilder.record_iter_changes handles changed symlinks.
979
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
980
    def _commit_sprout(self, tree, name):
981
        tree.add([name], [name + 'id'])
982
        rev_id = tree.commit('')
983
        return rev_id, tree.bzrdir.sprout('t2').open_workingtree()
984
985
    def _rename_in_tree(self, tree, name):
986
        tree.rename_one(name, 'new_' + name)
987
        return tree.commit('')
988
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
989
    def _commit_sprout_rename_merge(self, tree1, name, expect_fs_hash=False,
990
        mini_commit=None):
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
991
        """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.
992
        rev1, tree2 = self._commit_sprout(tree1, name)
993
        # change both sides equally
994
        rev2 = self._rename_in_tree(tree1, name)
995
        rev3 = self._rename_in_tree(tree2, name)
996
        tree1.merge_from_branch(tree2.branch)
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
997
        if mini_commit is None:
998
            mini_commit = self.mini_commit
999
        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.
1000
            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.
1001
        tree3, = self._get_revtrees(tree1, [rev4])
1002
        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.
1003
        file_id = name + 'id'
1004
        expected_graph = {}
1005
        expected_graph[(file_id, rev1)] = ()
1006
        expected_graph[(file_id, rev2)] = ((file_id, rev1),)
1007
        expected_graph[(file_id, rev3)] = ((file_id, rev1),)
1008
        expected_graph[(file_id, rev4)] = ((file_id, rev2), (file_id, rev3),)
1009
        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.
1010
1011
    def test_last_modified_revision_after_merge_dir_changes(self):
1012
        # merge a dir changes the last modified.
1013
        tree1 = self.make_branch_and_tree('t1')
1014
        self.build_tree(['t1/dir/'])
1015
        self._commit_sprout_rename_merge(tree1, 'dir')
1016
3775.2.19 by Robert Collins
CommitBuilder.record_iter_changes handles merged directories.
1017
    def test_last_modified_revision_after_merge_dir_changes_ric(self):
1018
        # merge a dir changes the last modified.
1019
        tree1 = self.make_branch_and_tree('t1')
1020
        self.build_tree(['t1/dir/'])
1021
        self._commit_sprout_rename_merge(tree1, 'dir',
1022
            mini_commit=self.mini_commit_record_iter_changes)
1023
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1024
    def test_last_modified_revision_after_merge_file_changes(self):
1025
        # merge a file changes the last modified.
1026
        tree1 = self.make_branch_and_tree('t1')
1027
        self.build_tree(['t1/file'])
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1028
        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.
1029
3775.2.20 by Robert Collins
CommitBuilder.record_iter_changes handles merged files.
1030
    def test_last_modified_revision_after_merge_file_changes_ric(self):
1031
        # merge a file changes the last modified.
1032
        tree1 = self.make_branch_and_tree('t1')
1033
        self.build_tree(['t1/file'])
1034
        self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True,
1035
            mini_commit=self.mini_commit_record_iter_changes)
1036
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1037
    def test_last_modified_revision_after_merge_link_changes(self):
1038
        # merge a link changes the last modified.
1039
        self.requireFeature(tests.SymlinkFeature)
1040
        tree1 = self.make_branch_and_tree('t1')
1041
        os.symlink('target', 't1/link')
1042
        self._commit_sprout_rename_merge(tree1, 'link')
1043
3775.2.21 by Robert Collins
CommitBuilder.record_iter_changes handles merged symlinks.
1044
    def test_last_modified_revision_after_merge_link_changes_ric(self):
1045
        # merge a link changes the last modified.
1046
        self.requireFeature(tests.SymlinkFeature)
1047
        tree1 = self.make_branch_and_tree('t1')
1048
        os.symlink('target', 't1/link')
1049
        self._commit_sprout_rename_merge(tree1, 'link',
1050
            mini_commit=self.mini_commit_record_iter_changes)
1051
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1052
    def _commit_sprout_rename_merge_converged(self, tree1, name,
1053
        mini_commit=None):
1054
        # Make a merge which just incorporates a change from a branch:
1055
        # The per-file graph is straight line, and no alteration occurs
1056
        # in the inventory.
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
1057
        # 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.
1058
        rev1, tree2 = self._commit_sprout(tree1, name)
1059
        # change on the other side to merge back
1060
        rev2 = self._rename_in_tree(tree2, name)
1061
        tree1.merge_from_branch(tree2.branch)
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1062
        if mini_commit is None:
1063
            mini_commit = self.mini_commit
4183.5.9 by Robert Collins
Fix creating new revisions of files when merging.
1064
        def _check_graph(in_tree, changed_in_tree):
1065
            rev3 = mini_commit(in_tree, name, 'new_' + name, False,
1066
                delta_against_basis=changed_in_tree)
1067
            tree3, = self._get_revtrees(in_tree, [rev2])
1068
            self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
1069
            file_id = name + 'id'
1070
            expected_graph = {}
1071
            expected_graph[(file_id, rev1)] = ()
1072
            expected_graph[(file_id, rev2)] = ((file_id, rev1),)
1073
            self.assertFileGraph(expected_graph, in_tree, (file_id, rev2))
1074
        _check_graph(tree1, True)
1075
        # Part 2: change in the merged into branch - we use tree2 that has a
1076
        # change to name, branch tree1 and give it an unrelated change, then
1077
        # merge that to t2.
1078
        other_tree = tree1.bzrdir.sprout('t3').open_workingtree()
1079
        other_rev = other_tree.commit('')
1080
        tree2.merge_from_branch(other_tree.branch)
1081
        _check_graph(tree2, False)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1082
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1083
    def _commit_sprout_make_merge(self, tree1, make, mini_commit=None):
1084
        # Make a merge which incorporates the addition of a new object to
1085
        # another branch. The per-file graph shows no additional change
1086
        # in the merge because its a straight line.
1087
        rev1 = tree1.commit('')
1088
        tree2 = tree1.bzrdir.sprout('t2').open_workingtree()
1089
        # make and commit on the other side to merge back
1090
        make('t2/name')
1091
        file_id = 'nameid'
1092
        tree2.add(['name'], [file_id])
1093
        rev2 = tree2.commit('')
1094
        tree1.merge_from_branch(tree2.branch)
1095
        if mini_commit is None:
1096
            mini_commit = self.mini_commit
1097
        rev3 = mini_commit(tree1, None, 'name', False)
1098
        tree3, = self._get_revtrees(tree1, [rev2])
1099
        # in rev2, name should be only changed in rev2
1100
        self.assertEqual(rev2, tree3.inventory[file_id].revision)
1101
        expected_graph = {}
1102
        expected_graph[(file_id, rev2)] = ()
1103
        self.assertFileGraph(expected_graph, tree1, (file_id, rev2))
1104
1105
    def test_last_modified_revision_after_converged_merge_dir_unchanged(self):
1106
        # 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.
1107
        tree1 = self.make_branch_and_tree('t1')
1108
        self.build_tree(['t1/dir/'])
1109
        self._commit_sprout_rename_merge_converged(tree1, 'dir')
1110
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1111
    def test_last_modified_revision_after_converged_merge_dir_unchanged_ric(self):
1112
        # merge a dir that changed preserves the last modified.
3775.2.22 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch directories.
1113
        tree1 = self.make_branch_and_tree('t1')
1114
        self.build_tree(['t1/dir/'])
1115
        self._commit_sprout_rename_merge_converged(tree1, 'dir',
1116
            mini_commit=self.mini_commit_record_iter_changes)
1117
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1118
    def test_last_modified_revision_after_converged_merge_file_unchanged(self):
1119
        # 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.
1120
        tree1 = self.make_branch_and_tree('t1')
1121
        self.build_tree(['t1/file'])
1122
        self._commit_sprout_rename_merge_converged(tree1, 'file')
1123
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1124
    def test_last_modified_revision_after_converged_merge_file_unchanged_ric(self):
1125
        # merge a file that changed preserves the last modified.
3775.2.23 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch files.
1126
        tree1 = self.make_branch_and_tree('t1')
1127
        self.build_tree(['t1/file'])
1128
        self._commit_sprout_rename_merge_converged(tree1, 'file',
1129
            mini_commit=self.mini_commit_record_iter_changes)
1130
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1131
    def test_last_modified_revision_after_converged_merge_link_unchanged(self):
1132
        # 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.
1133
        self.requireFeature(tests.SymlinkFeature)
1134
        tree1 = self.make_branch_and_tree('t1')
1135
        os.symlink('target', 't1/link')
1136
        self._commit_sprout_rename_merge_converged(tree1, 'link')
1137
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1138
    def test_last_modified_revision_after_converged_merge_link_unchanged_ric(self):
1139
        # merge a link that changed preserves the last modified.
3775.2.24 by Robert Collins
CommitBuilder.record_iter_changes handles changed-in-branch symlinks.
1140
        self.requireFeature(tests.SymlinkFeature)
1141
        tree1 = self.make_branch_and_tree('t1')
1142
        os.symlink('target', 't1/link')
1143
        self._commit_sprout_rename_merge_converged(tree1, 'link',
1144
            mini_commit=self.mini_commit_record_iter_changes)
1145
3775.2.33 by Robert Collins
Fix bug with merges of new files, increasing test coverage to ensure its kept fixed.
1146
    def test_last_modified_revision_after_merge_new_dir_unchanged(self):
1147
        # merge a new dir does not change the last modified.
1148
        tree1 = self.make_branch_and_tree('t1')
1149
        self._commit_sprout_make_merge(tree1, self.make_dir)
1150
1151
    def test_last_modified_revision_after_merge_new_dir_unchanged_ric(self):
1152
        # merge a new dir does not change the last modified.
1153
        tree1 = self.make_branch_and_tree('t1')
1154
        self._commit_sprout_make_merge(tree1, self.make_dir,
1155
            mini_commit=self.mini_commit_record_iter_changes)
1156
1157
    def test_last_modified_revision_after_merge_new_file_unchanged(self):
1158
        # merge a new file does not change the last modified.
1159
        tree1 = self.make_branch_and_tree('t1')
1160
        self._commit_sprout_make_merge(tree1, self.make_file)
1161
1162
    def test_last_modified_revision_after_merge_new_file_unchanged_ric(self):
1163
        # merge a new file does not change the last modified.
1164
        tree1 = self.make_branch_and_tree('t1')
1165
        self._commit_sprout_make_merge(tree1, self.make_file,
1166
            mini_commit=self.mini_commit_record_iter_changes)
1167
1168
    def test_last_modified_revision_after_merge_new_link_unchanged(self):
1169
        # merge a new link does not change the last modified.
1170
        tree1 = self.make_branch_and_tree('t1')
1171
        self._commit_sprout_make_merge(tree1, self.make_link)
1172
1173
    def test_last_modified_revision_after_merge_new_link_unchanged_ric(self):
1174
        # merge a new link does not change the last modified.
1175
        tree1 = self.make_branch_and_tree('t1')
1176
        self._commit_sprout_make_merge(tree1, self.make_link,
1177
            mini_commit=self.mini_commit_record_iter_changes)
1178
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1179
    def make_dir(self, name):
1180
        self.build_tree([name + '/'])
1181
1182
    def make_file(self, name):
1183
        self.build_tree([name])
1184
1185
    def make_link(self, name):
1186
        self.requireFeature(tests.SymlinkFeature)
1187
        os.symlink('target', name)
1188
4183.5.2 by Robert Collins
Support tree-reference in record_iter_changes.
1189
    def make_reference(self, name):
1190
        tree = self.make_branch_and_tree(name, format='1.9-rich-root')
1191
        tree.commit('foo')
1192
        return tree
1193
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1194
    def _check_kind_change(self, make_before, make_after, expect_fs_hash=False,
1195
        mini_commit=None):
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1196
        tree = self.make_branch_and_tree('.')
1197
        path = 'name'
1198
        make_before(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
1199
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1200
        def change_kind():
2831.5.2 by Vincent Ladeuil
Review feedback.
1201
            osutils.delete_any(path)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1202
            make_after(path)
2831.5.2 by Vincent Ladeuil
Review feedback.
1203
3709.3.1 by Robert Collins
First cut - make it work - at updating the tree stat cache during commit.
1204
        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.
1205
            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.
1206
1207
    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.
1208
        self._check_kind_change(self.make_dir, self.make_file,
1209
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1210
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1211
    def test_last_modified_dir_file_ric(self):
1212
        self._check_kind_change(self.make_dir, self.make_file,
1213
            expect_fs_hash=True,
1214
            mini_commit=self.mini_commit_record_iter_changes)
1215
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1216
    def test_last_modified_dir_link(self):
1217
        self._check_kind_change(self.make_dir, self.make_link)
1218
3775.2.25 by Robert Collins
CommitBuilder.record_iter_changes handles directories becoming files and links.
1219
    def test_last_modified_dir_link_ric(self):
1220
        self._check_kind_change(self.make_dir, self.make_link,
1221
            mini_commit=self.mini_commit_record_iter_changes)
1222
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1223
    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.
1224
        self._check_kind_change(self.make_link, self.make_file,
1225
            expect_fs_hash=True)
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1226
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
1227
    def test_last_modified_link_file_ric(self):
1228
        self._check_kind_change(self.make_link, self.make_file,
1229
            expect_fs_hash=True,
1230
            mini_commit=self.mini_commit_record_iter_changes)
1231
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1232
    def test_last_modified_link_dir(self):
1233
        self._check_kind_change(self.make_link, self.make_dir)
1234
3775.2.26 by Robert Collins
CommitBuilder.record_iter_changes handles links becomes directories and files.
1235
    def test_last_modified_link_dir_ric(self):
1236
        self._check_kind_change(self.make_link, self.make_dir,
1237
            mini_commit=self.mini_commit_record_iter_changes)
1238
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1239
    def test_last_modified_file_dir(self):
1240
        self._check_kind_change(self.make_file, self.make_dir)
1241
3775.2.27 by Robert Collins
CommitBuilder.record_iter_changes handles files becoming directories and links.
1242
    def test_last_modified_file_dir_ric(self):
1243
        self._check_kind_change(self.make_file, self.make_dir,
1244
            mini_commit=self.mini_commit_record_iter_changes)
1245
2776.1.5 by Robert Collins
Add reasonably comprehensive tests for path last modified and per file graph behaviour.
1246
    def test_last_modified_file_link(self):
1247
        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'.
1248
3775.2.27 by Robert Collins
CommitBuilder.record_iter_changes handles files becoming directories and links.
1249
    def test_last_modified_file_link_ric(self):
1250
        self._check_kind_change(self.make_file, self.make_link,
1251
            mini_commit=self.mini_commit_record_iter_changes)
3775.2.28 by Robert Collins
Merge .dev.
1252
3831.1.1 by John Arbash Meinel
Before allowing commit to succeed, verify the texts will be 'safe'.
1253
    def test_get_commit_builder_with_invalid_revprops(self):
1254
        branch = self.make_branch('.')
1255
        branch.repository.lock_write()
1256
        self.addCleanup(branch.repository.unlock)
1257
        self.assertRaises(ValueError, branch.repository.get_commit_builder,
1258
            branch, [], branch.get_config(),
1259
            revprops={'invalid': u'property\rwith\r\ninvalid chars'})
1260
1261
    def test_commit_builder_commit_with_invalid_message(self):
1262
        branch = self.make_branch('.')
1263
        branch.repository.lock_write()
1264
        self.addCleanup(branch.repository.unlock)
1265
        builder = branch.repository.get_commit_builder(branch, [],
1266
            branch.get_config())
1267
        self.addCleanup(branch.repository.abort_write_group)
1268
        self.assertRaises(ValueError, builder.commit,
1269
            u'Invalid\r\ncommit message\r\n')