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