~bzr-pqm/bzr/bzr.dev

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