~bzr-pqm/bzr/bzr.dev

5128.1.1 by Vincent Ladeuil
Uncontroversial cleanups, mostly comments
1
# Copyright (C) 2006-2010 Canonical Ltd
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
2
# Authors:  Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
17
18
import os
19
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
20
from bzrlib import (
21
    branch,
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
22
    conflicts,
6472.2.2 by Jelmer Vernooij
Use controldir rather than bzrdir in a couple more places.
23
    controldir,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
24
    errors,
3335.1.3 by Jelmer Vernooij
Add tests for start_commit hook.
25
    mutabletree,
2922.2.1 by John Arbash Meinel
Add failing tests exposing part of bug #114615
26
    osutils,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
27
    revision as _mod_revision,
5777.2.1 by Jelmer Vernooij
Skip commit exclude tests for repositories that don't support excludes.
28
    tests,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
29
    ui,
30
    )
4523.1.4 by Martin Pool
Rename remaining *_implementations tests
31
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
5422.1.5 by Martin Pool
Move ProgressRecordingUIFactory to bzrlib.tests.testui
32
from bzrlib.tests.testui import ProgressRecordingUIFactory
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
33
34
35
class TestCommit(TestCaseWithWorkingTree):
36
2922.2.1 by John Arbash Meinel
Add failing tests exposing part of bug #114615
37
    def test_autodelete_renamed(self):
38
        tree_a = self.make_branch_and_tree('a')
39
        self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2'])
40
        tree_a.add(['dir', 'dir/f1', 'dir/f2'], ['dir-id', 'f1-id', 'f2-id'])
41
        rev_id1 = tree_a.commit('init')
42
        # Start off by renaming entries,
43
        # but then actually auto delete the whole tree
44
        # https://bugs.launchpad.net/bzr/+bug/114615
45
        tree_a.rename_one('dir/f1', 'dir/a')
46
        tree_a.rename_one('dir/f2', 'dir/z')
47
        osutils.rmtree('a/dir')
48
        tree_a.commit('autoremoved')
49
50
        tree_a.lock_read()
51
        try:
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
52
            root_id = tree_a.get_root_id()
2922.2.1 by John Arbash Meinel
Add failing tests exposing part of bug #114615
53
            paths = [(path, ie.file_id)
54
                     for path, ie in tree_a.iter_entries_by_dir()]
55
        finally:
56
            tree_a.unlock()
57
        # The only paths left should be the root
58
        self.assertEqual([('', root_id)], paths)
59
2922.2.3 by John Arbash Meinel
Add a test which shows why the previous fix is broken.
60
    def test_no_autodelete_renamed_away(self):
61
        tree_a = self.make_branch_and_tree('a')
62
        self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2', 'a/dir2/'])
63
        tree_a.add(['dir', 'dir/f1', 'dir/f2', 'dir2'],
64
                   ['dir-id', 'f1-id', 'f2-id', 'dir2-id'])
65
        rev_id1 = tree_a.commit('init')
66
        # Rename one entry out of this directory
67
        tree_a.rename_one('dir/f1', 'dir2/a')
68
        osutils.rmtree('a/dir')
69
        tree_a.commit('autoremoved')
70
71
        tree_a.lock_read()
72
        try:
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
73
            root_id = tree_a.get_root_id()
2922.2.3 by John Arbash Meinel
Add a test which shows why the previous fix is broken.
74
            paths = [(path, ie.file_id)
75
                     for path, ie in tree_a.iter_entries_by_dir()]
76
        finally:
77
            tree_a.unlock()
78
        # The only paths left should be the root
79
        self.assertEqual([('', root_id), ('dir2', 'dir2-id'),
80
                          ('dir2/a', 'f1-id'),
81
                         ], paths)
82
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
83
    def test_no_autodelete_alternate_renamed(self):
84
        # Test for bug #114615
85
        tree_a = self.make_branch_and_tree('A')
86
        self.build_tree(['A/a/', 'A/a/m', 'A/a/n'])
87
        tree_a.add(['a', 'a/m', 'a/n'], ['a-id', 'm-id', 'n-id'])
88
        tree_a.commit('init')
89
90
        tree_a.lock_read()
91
        try:
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
92
            root_id = tree_a.get_root_id()
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
93
        finally:
94
            tree_a.unlock()
95
96
        tree_b = tree_a.bzrdir.sprout('B').open_workingtree()
97
        self.build_tree(['B/xyz/'])
98
        tree_b.add(['xyz'], ['xyz-id'])
99
        tree_b.rename_one('a/m', 'xyz/m')
100
        osutils.rmtree('B/a')
101
        tree_b.commit('delete in B')
102
103
        paths = [(path, ie.file_id)
104
                 for path, ie in tree_b.iter_entries_by_dir()]
105
        self.assertEqual([('', root_id),
106
                          ('xyz', 'xyz-id'),
107
                          ('xyz/m', 'm-id'),
108
                         ], paths)
109
110
        self.build_tree_contents([('A/a/n', 'new contents for n\n')])
111
        tree_a.commit('change n in A')
112
113
        # Merging from A should introduce conflicts because 'n' was modified
5128.1.1 by Vincent Ladeuil
Uncontroversial cleanups, mostly comments
114
        # (in A) and removed (in B), so 'a' needs to be restored.
2922.2.4 by John Arbash Meinel
Fix bug #114615 by teaching unversion() to not touch renamed entries.
115
        num_conflicts = tree_b.merge_from_branch(tree_a.branch)
116
        self.assertEqual(3, num_conflicts)
117
        paths = [(path, ie.file_id)
118
                 for path, ie in tree_b.iter_entries_by_dir()]
119
        self.assertEqual([('', root_id),
120
                          ('a', 'a-id'),
121
                          ('xyz', 'xyz-id'),
122
                          ('a/n.OTHER', 'n-id'),
123
                          ('xyz/m', 'm-id'),
124
                         ], paths)
125
        osutils.rmtree('B/a')
126
        try:
127
            # bzr resolve --all
128
            tree_b.set_conflicts(conflicts.ConflictList())
129
        except errors.UnsupportedOperation:
130
            # On WT2, set_conflicts is unsupported, but the rmtree has the same
131
            # effect.
132
            pass
133
        tree_b.commit('autoremove a, without touching xyz/m')
134
        paths = [(path, ie.file_id)
135
                 for path, ie in tree_b.iter_entries_by_dir()]
136
        self.assertEqual([('', root_id),
137
                          ('xyz', 'xyz-id'),
138
                          ('xyz/m', 'm-id'),
139
                         ], paths)
140
3602.1.1 by Robert Collins
Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins)
141
    def test_commit_exclude_pending_merge_fails(self):
142
        """Excludes are a form of partial commit."""
143
        wt = self.make_branch_and_tree('.')
144
        self.build_tree(['foo'])
145
        wt.add('foo')
146
        wt.commit('commit one')
147
        wt2 = wt.bzrdir.sprout('to').open_workingtree()
148
        wt2.commit('change_right')
149
        wt.merge_from_branch(wt2.branch)
5777.2.1 by Jelmer Vernooij
Skip commit exclude tests for repositories that don't support excludes.
150
        try:
151
            self.assertRaises(errors.CannotCommitSelectedFileMerge,
152
                wt.commit, 'test', exclude=['foo'])
153
        except errors.ExcludesUnsupported:
154
            raise tests.TestNotApplicable("excludes not supported by this "
155
                "repository format")
3602.1.1 by Robert Collins
Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins)
156
3602.1.2 by Robert Collins
Review feedback : test for PointlessCommit and that the example given in the help (excluding a subtree of a specified tree) does in fact work.
157
    def test_commit_exclude_exclude_changed_is_pointless(self):
158
        tree = self.make_branch_and_tree('.')
159
        self.build_tree(['a'])
160
        tree.smart_add(['.'])
161
        tree.commit('setup test')
162
        self.build_tree_contents([('a', 'new contents for "a"\n')])
5777.2.1 by Jelmer Vernooij
Skip commit exclude tests for repositories that don't support excludes.
163
        try:
164
            self.assertRaises(errors.PointlessCommit, tree.commit, 'test',
165
                exclude=['a'], allow_pointless=False)
166
        except errors.ExcludesUnsupported:
167
            raise tests.TestNotApplicable("excludes not supported by this "
168
                "repository format")
3602.1.2 by Robert Collins
Review feedback : test for PointlessCommit and that the example given in the help (excluding a subtree of a specified tree) does in fact work.
169
3602.1.1 by Robert Collins
Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins)
170
    def test_commit_exclude_excludes_modified_files(self):
171
        tree = self.make_branch_and_tree('.')
172
        self.build_tree(['a', 'b', 'c'])
173
        tree.smart_add(['.'])
5777.2.1 by Jelmer Vernooij
Skip commit exclude tests for repositories that don't support excludes.
174
        try:
175
            tree.commit('test', exclude=['b', 'c'])
176
        except errors.ExcludesUnsupported:
177
            raise tests.TestNotApplicable("excludes not supported by this "
178
                "repository format")
3602.1.4 by Robert Collins
Andrew's review feedback.
179
        # If b was excluded it will still be 'added' in status.
3602.1.1 by Robert Collins
Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins)
180
        tree.lock_read()
181
        self.addCleanup(tree.unlock)
182
        changes = list(tree.iter_changes(tree.basis_tree()))
183
        self.assertEqual(2, len(changes))
184
        self.assertEqual((None, 'b'), changes[0][1])
185
        self.assertEqual((None, 'c'), changes[1][1])
186
3602.1.2 by Robert Collins
Review feedback : test for PointlessCommit and that the example given in the help (excluding a subtree of a specified tree) does in fact work.
187
    def test_commit_exclude_subtree_of_selected(self):
188
        tree = self.make_branch_and_tree('.')
189
        self.build_tree(['a/', 'a/b'])
190
        tree.smart_add(['.'])
5777.2.1 by Jelmer Vernooij
Skip commit exclude tests for repositories that don't support excludes.
191
        try:
192
            tree.commit('test', specific_files=['a'], exclude=['a/b'])
193
        except errors.ExcludesUnsupported:
194
            raise tests.TestNotApplicable("excludes not supported by this "
195
                "repository format")
3602.1.4 by Robert Collins
Andrew's review feedback.
196
        # If a/b was excluded it will still be 'added' in status.
3602.1.2 by Robert Collins
Review feedback : test for PointlessCommit and that the example given in the help (excluding a subtree of a specified tree) does in fact work.
197
        tree.lock_read()
198
        self.addCleanup(tree.unlock)
199
        changes = list(tree.iter_changes(tree.basis_tree()))
200
        self.assertEqual(1, len(changes))
201
        self.assertEqual((None, 'a/b'), changes[0][1])
202
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
203
    def test_commit_sets_last_revision(self):
204
        tree = self.make_branch_and_tree('tree')
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
205
        committed_id = tree.commit('foo', rev_id='foo')
1908.7.6 by Robert Collins
Deprecate WorkingTree.last_revision.
206
        self.assertEqual(['foo'], tree.get_parent_ids())
1773.1.1 by Robert Collins
Teach WorkingTree.commit to return the committed revision id.
207
        # the commit should have returned the same id we asked for.
208
        self.assertEqual('foo', committed_id)
209
210
    def test_commit_returns_revision_id(self):
211
        tree = self.make_branch_and_tree('.')
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
212
        committed_id = tree.commit('message')
1773.1.1 by Robert Collins
Teach WorkingTree.commit to return the committed revision id.
213
        self.assertTrue(tree.branch.repository.has_revision(committed_id))
214
        self.assertNotEqual(None, committed_id)
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
215
216
    def test_commit_local_unbound(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
217
        # using the library api to do a local commit on unbound branches is
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
218
        # also an error
219
        tree = self.make_branch_and_tree('tree')
220
        self.assertRaises(errors.LocalRequiresBoundBranch,
221
                          tree.commit,
222
                          'foo',
223
                          local=True)
2374.2.1 by John Arbash Meinel
(broken) merge a test case showing that commiting a merge of a kind change fails.
224
225
    def test_commit_merged_kind_change(self):
226
        """Test merging a kind change.
227
228
        Test making a kind change in a working tree, and then merging that
229
        from another. When committed it should commit the new kind.
230
        """
231
        wt = self.make_branch_and_tree('.')
232
        self.build_tree(['a'])
233
        wt.add(['a'])
234
        wt.commit('commit one')
235
        wt2 = wt.bzrdir.sprout('to').open_workingtree()
236
        os.remove('a')
237
        os.mkdir('a')
238
        wt.commit('changed kind')
239
        wt2.merge_from_branch(wt.branch)
240
        wt2.commit('merged kind change')
241
4536.3.1 by Robert Collins
Defer doing unversioning of file ids during commit to after completing branch operations. (Robert Collins, bug 282402)
242
    def test_commit_aborted_does_not_apply_automatic_changes_bug_282402(self):
243
        wt = self.make_branch_and_tree('.')
244
        wt.add(['a'], ['a-id'], ['file'])
245
        def fail_message(obj):
246
            raise errors.BzrCommandError("empty commit message")
247
        self.assertRaises(errors.BzrCommandError, wt.commit,
248
            message_callback=fail_message)
249
        self.assertEqual('a', wt.id2path('a-id'))
250
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
251
    def test_local_commit_ignores_master(self):
252
        # a --local commit does not require access to the master branch
253
        # at all, or even for it to exist.
254
        # we test this by setting up a bound branch and then corrupting
255
        # the master.
256
        master = self.make_branch('master')
257
        tree = self.make_branch_and_tree('tree')
258
        try:
259
            tree.branch.bind(master)
260
        except errors.UpgradeRequired:
261
            # older format.
262
            return
1955.3.14 by John Arbash Meinel
Correctly fix the workingtree put() test fixes
263
        master.bzrdir.transport.put_bytes('branch-format', 'garbage')
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
264
        del master
265
        # check its corrupted.
266
        self.assertRaises(errors.UnknownFormatError,
6472.2.2 by Jelmer Vernooij
Use controldir rather than bzrdir in a couple more places.
267
                          controldir.ControlDir.open,
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
268
                          'master')
269
        tree.commit('foo', rev_id='foo', local=True)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
270
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
271
    def test_local_commit_does_not_push_to_master(self):
272
        # a --local commit does not require access to the master branch
273
        # at all, or even for it to exist.
274
        # we test that even when its available it does not push to it.
275
        master = self.make_branch('master')
276
        tree = self.make_branch_and_tree('tree')
277
        try:
278
            tree.branch.bind(master)
279
        except errors.UpgradeRequired:
280
            # older format.
281
            return
282
        tree.commit('foo', rev_id='foo', local=True)
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
283
        self.assertFalse(master.repository.has_revision('foo'))
2598.5.7 by Aaron Bentley
Updates from review
284
        self.assertEqual(_mod_revision.NULL_REVISION,
285
                         (_mod_revision.ensure_null(master.last_revision())))
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
286
287
    def test_record_initial_ghost(self):
288
        """The working tree needs to record ghosts during commit."""
289
        wt = self.make_branch_and_tree('.')
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
290
        wt.set_parent_ids(['non:existent@rev--ision--0--2'],
291
            allow_leftmost_as_ghost=True)
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
292
        rev_id = wt.commit('commit against a ghost first parent.')
293
        rev = wt.branch.repository.get_revision(rev_id)
294
        self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
295
        # parent_sha1s is not populated now, WTF. rbc 20051003
296
        self.assertEqual(len(rev.parent_sha1s), 0)
297
298
    def test_record_two_ghosts(self):
299
        """The working tree should preserve all the parents during commit."""
300
        wt = self.make_branch_and_tree('.')
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
301
        wt.set_parent_ids([
302
                'foo@azkhazan-123123-abcabc',
303
                'wibble@fofof--20050401--1928390812',
304
            ],
305
            allow_leftmost_as_ghost=True)
1927.2.1 by Robert Collins
Alter set_pending_merges to shove the left most merge into the trees last-revision if that is not set. Related bugfixes include basis_tree handling ghosts, de-duping the merges with the last-revision and update changing where and how it adds its pending merge.
306
        rev_id = wt.commit("commit from ghost base with one merge")
307
        # the revision should have been committed with two parents
308
        rev = wt.branch.repository.get_revision(rev_id)
309
        self.assertEqual(['foo@azkhazan-123123-abcabc',
310
            'wibble@fofof--20050401--1928390812'],
311
            rev.parent_ids)
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
312
1988.3.1 by Robert Collins
Add test case to ensure that the working tree inventory and disk state is correctly update when commit is removing directory entries.
313
    def test_commit_deleted_subtree_and_files_updates_workingtree(self):
314
        """The working trees inventory may be adjusted by commit."""
315
        wt = self.make_branch_and_tree('.')
316
        wt.lock_write()
317
        self.build_tree(['a', 'b/', 'b/c', 'd'])
318
        wt.add(['a', 'b', 'b/c', 'd'], ['a-id', 'b-id', 'c-id', 'd-id'])
319
        this_dir = self.get_transport()
320
        this_dir.delete_tree('b')
321
        this_dir.delete('d')
322
        # now we have a tree with a through d in the inventory, but only
323
        # a present on disk. After commit b-id, c-id and d-id should be
324
        # missing from the inventory, within the same tree transaction.
325
        wt.commit('commit stuff')
326
        self.assertTrue(wt.has_id('a-id'))
327
        self.assertFalse(wt.has_or_had_id('b-id'))
328
        self.assertFalse(wt.has_or_had_id('c-id'))
329
        self.assertFalse(wt.has_or_had_id('d-id'))
330
        self.assertTrue(wt.has_filename('a'))
331
        self.assertFalse(wt.has_filename('b'))
332
        self.assertFalse(wt.has_filename('b/c'))
333
        self.assertFalse(wt.has_filename('d'))
334
        wt.unlock()
335
        # the changes should have persisted to disk - reopen the workingtree
336
        # to be sure.
337
        wt = wt.bzrdir.open_workingtree()
338
        wt.lock_read()
339
        self.assertTrue(wt.has_id('a-id'))
340
        self.assertFalse(wt.has_or_had_id('b-id'))
341
        self.assertFalse(wt.has_or_had_id('c-id'))
342
        self.assertFalse(wt.has_or_had_id('d-id'))
343
        self.assertTrue(wt.has_filename('a'))
344
        self.assertFalse(wt.has_filename('b'))
345
        self.assertFalse(wt.has_filename('b/c'))
346
        self.assertFalse(wt.has_filename('d'))
347
        wt.unlock()
1731.2.4 by Aaron Bentley
Ensure subsume works with Knit2 repos
348
2363.2.2 by John Arbash Meinel
Simplify the test even further....
349
    def test_commit_deleted_subtree_with_removed(self):
2363.2.1 by John Arbash Meinel
(broken) Add a simplified test which exposes the bug.
350
        wt = self.make_branch_and_tree('.')
351
        self.build_tree(['a', 'b/', 'b/c', 'd'])
352
        wt.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
353
        wt.commit('first')
2363.2.2 by John Arbash Meinel
Simplify the test even further....
354
        wt.remove('b/c')
2363.2.1 by John Arbash Meinel
(broken) Add a simplified test which exposes the bug.
355
        this_dir = self.get_transport()
356
        this_dir.delete_tree('b')
357
        wt.lock_write()
358
        wt.commit('commit deleted rename')
359
        self.assertTrue(wt.has_id('a-id'))
360
        self.assertFalse(wt.has_or_had_id('b-id'))
361
        self.assertFalse(wt.has_or_had_id('c-id'))
362
        self.assertTrue(wt.has_filename('a'))
363
        self.assertFalse(wt.has_filename('b'))
364
        self.assertFalse(wt.has_filename('b/c'))
365
        wt.unlock()
366
1731.2.4 by Aaron Bentley
Ensure subsume works with Knit2 repos
367
    def test_commit_move_new(self):
368
        wt = self.make_branch_and_tree('first')
369
        wt.commit('first')
370
        wt2 = wt.bzrdir.sprout('second').open_workingtree()
371
        self.build_tree(['second/name1'])
372
        wt2.add('name1', 'name1-id')
373
        wt2.commit('second')
374
        wt.merge_from_branch(wt2.branch)
375
        wt.rename_one('name1', 'name2')
376
        wt.commit('third')
377
        wt.path2id('name1-id')
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
378
379
    def test_nested_commit(self):
380
        """Commit in multiply-nested trees"""
381
        tree = self.make_branch_and_tree('.')
382
        if not tree.supports_tree_reference():
383
            # inapplicable test.
384
            return
385
        subtree = self.make_branch_and_tree('subtree')
386
        subsubtree = self.make_branch_and_tree('subtree/subtree')
387
        subtree.add(['subtree'])
388
        tree.add(['subtree'])
389
        # use allow_pointless=False to ensure that the deepest tree, which
390
        # has no commits made to it, does not get a pointless commit.
391
        rev_id = tree.commit('added reference', allow_pointless=False)
392
        tree.lock_read()
393
        self.addCleanup(tree.unlock)
394
        # the deepest subtree has not changed, so no commit should take place.
2598.5.10 by Aaron Bentley
Return NULL_REVISION instead of None for the null revision
395
        self.assertEqual('null:', subsubtree.last_revision())
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
396
        # the intermediate tree should have committed a pointer to the current
397
        # subtree revision.
398
        sub_basis = subtree.basis_tree()
399
        sub_basis.lock_read()
400
        self.addCleanup(sub_basis.unlock)
401
        self.assertEqual(subsubtree.last_revision(),
2255.2.227 by Robert Collins
Make all test_commit tests pass.
402
            sub_basis.get_reference_revision(sub_basis.path2id('subtree')))
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
403
        # the intermediate tree has changed, so should have had a commit
404
        # take place.
405
        self.assertNotEqual(None, subtree.last_revision())
406
        # the outer tree should have committed a pointer to the current
407
        # subtree revision.
408
        basis = tree.basis_tree()
409
        basis.lock_read()
410
        self.addCleanup(basis.unlock)
411
        self.assertEqual(subtree.last_revision(),
2255.2.226 by Robert Collins
Get merge_nested finally working: change nested tree iterators to take file_ids, and ensure the right branch is connected to in the merge logic. May not be suitable for shared repositories yet.
412
            basis.get_reference_revision(basis.path2id('subtree')))
2255.2.218 by Robert Collins
Make the nested tree commit smoke test be more rigourous.
413
        # the outer tree must have have changed too.
414
        self.assertNotEqual(None, rev_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
415
2255.2.220 by Robert Collins
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.
416
    def test_nested_commit_second_commit_detects_changes(self):
417
        """Commit with a nested tree picks up the correct child revid."""
418
        tree = self.make_branch_and_tree('.')
419
        if not tree.supports_tree_reference():
420
            # inapplicable test.
421
            return
422
        subtree = self.make_branch_and_tree('subtree')
423
        tree.add(['subtree'])
424
        self.build_tree(['subtree/file'])
425
        subtree.add(['file'], ['file-id'])
426
        rev_id = tree.commit('added reference', allow_pointless=False)
4183.5.5 by Robert Collins
Enable record_iter_changes for cases where it can work.
427
        tree.get_reference_revision(tree.path2id('subtree'))
2255.2.220 by Robert Collins
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.
428
        child_revid = subtree.last_revision()
429
        # now change the child tree
430
        self.build_tree_contents([('subtree/file', 'new-content')])
431
        # and commit in the parent should commit the child and grab its revid,
432
        # we test with allow_pointless=False here so that we are simulating
433
        # what users will see.
434
        rev_id2 = tree.commit('changed subtree only', allow_pointless=False)
435
        # the child tree has changed, so should have had a commit
436
        # take place.
437
        self.assertNotEqual(None, subtree.last_revision())
438
        self.assertNotEqual(child_revid, subtree.last_revision())
439
        # the outer tree should have committed a pointer to the current
440
        # subtree revision.
441
        basis = tree.basis_tree()
442
        basis.lock_read()
443
        self.addCleanup(basis.unlock)
444
        self.assertEqual(subtree.last_revision(),
2255.2.226 by Robert Collins
Get merge_nested finally working: change nested tree iterators to take file_ids, and ensure the right branch is connected to in the merge logic. May not be suitable for shared repositories yet.
445
            basis.get_reference_revision(basis.path2id('subtree')))
2255.2.220 by Robert Collins
Fix failing detection of changes restricted to subtrees causing spurious pointless commit errors.
446
        self.assertNotEqual(rev_id, rev_id2)
447
2825.5.2 by Robert Collins
Review feedback, and fix pointless commits with nested trees to raise PointlessCommit appropriately.
448
    def test_nested_pointless_commits_are_pointless(self):
449
        tree = self.make_branch_and_tree('.')
450
        if not tree.supports_tree_reference():
451
            # inapplicable test.
452
            return
453
        subtree = self.make_branch_and_tree('subtree')
454
        tree.add(['subtree'])
455
        # record the reference.
456
        rev_id = tree.commit('added reference')
457
        child_revid = subtree.last_revision()
458
        # now do a no-op commit with allow_pointless=False
459
        self.assertRaises(errors.PointlessCommit, tree.commit, '',
460
            allow_pointless=False)
461
        self.assertEqual(child_revid, subtree.last_revision())
462
        self.assertEqual(rev_id, tree.last_revision())
463
1988.3.1 by Robert Collins
Add test case to ensure that the working tree inventory and disk state is correctly update when commit is removing directory entries.
464
1740.3.10 by Jelmer Vernooij
Fix some minor issues pointed out by j-a-m.
465
class TestCommitProgress(TestCaseWithWorkingTree):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
466
4985.2.1 by Vincent Ladeuil
Deploy addAttrCleanup on the whole test suite.
467
    def setUp(self):
468
        super(TestCommitProgress, self).setUp()
5422.1.4 by Martin Pool
Rename CapturingUIFactory to ProgressRecordingUIFactory
469
        ui.ui_factory = ProgressRecordingUIFactory()
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
470
471
    def test_commit_progress_steps(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
472
        # during commit we one progress update for every entry in the
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
473
        # inventory, and then one for the inventory, and one for the
474
        # inventory, and one for the revision insertions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
475
        # first we need a test commit to do. Lets setup a branch with
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
476
        # 3 files, and alter one in a selected-file commit. This exercises
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
477
        # a number of cases quickly. We should also test things like
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
478
        # selective commits which excludes newly added files.
479
        tree = self.make_branch_and_tree('.')
480
        self.build_tree(['a', 'b', 'c'])
481
        tree.add(['a', 'b', 'c'])
482
        tree.commit('first post')
483
        f = file('b', 'wt')
484
        f.write('new content')
485
        f.close()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
486
        # set a progress bar that captures the calls so we can see what is
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
487
        # emitted
5422.1.4 by Martin Pool
Rename CapturingUIFactory to ProgressRecordingUIFactory
488
        factory = ProgressRecordingUIFactory()
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
489
        ui.ui_factory = factory
490
        # TODO RBC 20060421 it would be nice to merge the reporter output
491
        # into the factory for this test - just make the test ui factory
492
        # pun as a reporter. Then we can check the ordering is right.
493
        tree.commit('second post', specific_files=['b'])
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
494
        # 5 steps, the first of which is reported 2 times, once per dir
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
495
        self.assertEqual(
4183.5.5 by Robert Collins
Enable record_iter_changes for cases where it can work.
496
            [('update', 1, 5, 'Collecting changes [0] - Stage'),
497
             ('update', 1, 5, 'Collecting changes [1] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
498
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
499
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
500
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
501
             ('update', 5, 5, 'Running post_commit hooks - Stage')],
1666.1.19 by Robert Collins
Introduce a progress bar during commit.
502
            factory._calls
503
           )
2553.1.2 by Robert Collins
Show hook names during commit.
504
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
505
    def test_commit_progress_shows_post_hook_names(self):
2553.1.2 by Robert Collins
Show hook names during commit.
506
        tree = self.make_branch_and_tree('.')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
507
        # set a progress bar that captures the calls so we can see what is
2553.1.2 by Robert Collins
Show hook names during commit.
508
        # emitted
5422.1.4 by Martin Pool
Rename CapturingUIFactory to ProgressRecordingUIFactory
509
        factory = ProgressRecordingUIFactory()
2553.1.2 by Robert Collins
Show hook names during commit.
510
        ui.ui_factory = factory
511
        def a_hook(_, _2, _3, _4, _5, _6):
512
            pass
3256.2.19 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.workingtree_implementations.test_commit.
513
        branch.Branch.hooks.install_named_hook('post_commit', a_hook,
514
                                               'hook name')
2553.1.2 by Robert Collins
Show hook names during commit.
515
        tree.commit('first post')
516
        self.assertEqual(
4183.5.5 by Robert Collins
Enable record_iter_changes for cases where it can work.
517
            [('update', 1, 5, 'Collecting changes [0] - Stage'),
518
             ('update', 1, 5, 'Collecting changes [1] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
519
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
520
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
521
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
522
             ('update', 5, 5, 'Running post_commit hooks - Stage'),
523
             ('update', 5, 5, 'Running post_commit hooks [hook name] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
524
             ],
525
            factory._calls
526
           )
527
528
    def test_commit_progress_shows_pre_hook_names(self):
529
        tree = self.make_branch_and_tree('.')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
530
        # set a progress bar that captures the calls so we can see what is
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
531
        # emitted
5422.1.4 by Martin Pool
Rename CapturingUIFactory to ProgressRecordingUIFactory
532
        factory = ProgressRecordingUIFactory()
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
533
        ui.ui_factory = factory
2659.3.3 by NamNguyen
Changed ``pre_commit`` hook signature.
534
        def a_hook(_, _2, _3, _4, _5, _6, _7, _8):
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
535
            pass
3256.2.19 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.workingtree_implementations.test_commit.
536
        branch.Branch.hooks.install_named_hook('pre_commit', a_hook,
537
                                               'hook name')
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
538
        tree.commit('first post')
539
        self.assertEqual(
4183.5.5 by Robert Collins
Enable record_iter_changes for cases where it can work.
540
            [('update', 1, 5, 'Collecting changes [0] - Stage'),
541
             ('update', 1, 5, 'Collecting changes [1] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
542
             ('update', 2, 5, 'Saving data locally - Stage'),
2659.3.9 by NamNguyen
branch.py:
543
             ('update', 3, 5, 'Running pre_commit hooks - Stage'),
544
             ('update', 3, 5, 'Running pre_commit hooks [hook name] - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
545
             ('update', 4, 5, 'Updating the working tree - Stage'),
2659.3.9 by NamNguyen
branch.py:
546
             ('update', 5, 5, 'Running post_commit hooks - Stage'),
2659.3.1 by NamNguyen
``Branch.hooks`` now supports ``pre_commit`` hook.
547
             ],
548
            factory._calls
549
           )
3335.1.3 by Jelmer Vernooij
Add tests for start_commit hook.
550
551
    def test_start_commit_hook(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
552
        """Make sure a start commit hook can modify the tree that is
3335.1.3 by Jelmer Vernooij
Add tests for start_commit hook.
553
        committed."""
554
        def start_commit_hook_adds_file(tree):
6437.20.3 by Wouter van Heyst
mechanically replace file().write() pattern with a with-keyword version
555
            with open(tree.abspath("newfile"), 'w') as f: f.write("data")
3335.1.3 by Jelmer Vernooij
Add tests for start_commit hook.
556
            tree.add(["newfile"])
557
        def restoreDefaults():
558
            mutabletree.MutableTree.hooks['start_commit'] = []
559
        self.addCleanup(restoreDefaults)
560
        tree = self.make_branch_and_tree('.')
3256.2.26 by Daniel Watkins
Updated tests to use install_named_hook.
561
        mutabletree.MutableTree.hooks.install_named_hook(
562
            'start_commit',
563
            start_commit_hook_adds_file,
564
            None)
3335.1.3 by Jelmer Vernooij
Add tests for start_commit hook.
565
        revid = tree.commit('first post')
566
        committed_tree = tree.basis_tree()
567
        self.assertTrue(committed_tree.has_filename("newfile"))
4634.33.1 by Ian Clatworthy
original finish_commit hook patch
568
4634.33.3 by Ian Clatworthy
review feedback from Robert: rename finish_commit to post_commit
569
    def test_post_commit_hook(self):
570
        """Make sure a post_commit hook is called after a commit."""
571
        def post_commit_hook_test_params(params):
4634.33.2 by Ian Clatworthy
review feedback from jam
572
            self.assertTrue(isinstance(params,
4634.33.3 by Ian Clatworthy
review feedback from Robert: rename finish_commit to post_commit
573
                mutabletree.PostCommitHookParams))
4634.33.2 by Ian Clatworthy
review feedback from jam
574
            self.assertTrue(isinstance(params.mutable_tree,
575
                mutabletree.MutableTree))
6437.20.3 by Wouter van Heyst
mechanically replace file().write() pattern with a with-keyword version
576
            with open(tree.abspath("newfile"), 'w') as f: f.write("data")
4634.33.2 by Ian Clatworthy
review feedback from jam
577
            params.mutable_tree.add(["newfile"])
4634.33.1 by Ian Clatworthy
original finish_commit hook patch
578
        tree = self.make_branch_and_tree('.')
579
        mutabletree.MutableTree.hooks.install_named_hook(
4634.33.3 by Ian Clatworthy
review feedback from Robert: rename finish_commit to post_commit
580
            'post_commit',
581
            post_commit_hook_test_params,
4634.33.1 by Ian Clatworthy
original finish_commit hook patch
582
            None)
583
        self.assertFalse(tree.has_filename("newfile"))
584
        revid = tree.commit('first post')
585
        self.assertTrue(tree.has_filename("newfile"))
586
        committed_tree = tree.basis_tree()
587
        self.assertFalse(committed_tree.has_filename("newfile"))