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