~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1246 by Martin Pool
- add very simple commit tests
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1246 by Martin Pool
- add very simple commit tests
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1246 by Martin Pool
- add very simple commit tests
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
import os
19
1472 by Robert Collins
post commit hook, first pass implementation
20
import bzrlib
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
21
from bzrlib import (
22
    errors,
23
    lockdir,
1959.4.3 by Aaron Bentley
Merge bzr.dev
24
    osutils,
25
    tests,
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
26
    )
1246 by Martin Pool
- add very simple commit tests
27
from bzrlib.branch import Branch
1614.1.1 by Aaron Bentley
Fixed master locking in commit
28
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
29
from bzrlib.commit import Commit, NullCommitReporter
1442.1.60 by Robert Collins
gpg sign commits if the policy says we need to
30
from bzrlib.config import BranchConfig
1614.1.1 by Aaron Bentley
Fixed master locking in commit
31
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
32
                           LockContention)
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
33
from bzrlib.tests import TestCaseWithTransport
34
from bzrlib.workingtree import WorkingTree
1246 by Martin Pool
- add very simple commit tests
35
36
1257 by Martin Pool
doc
37
# TODO: Test commit with some added, and added-but-missing files
38
1442.1.60 by Robert Collins
gpg sign commits if the policy says we need to
39
class MustSignConfig(BranchConfig):
40
41
    def signature_needed(self):
42
        return True
43
44
    def gpg_signing_command(self):
45
        return ['cat', '-']
46
47
1472 by Robert Collins
post commit hook, first pass implementation
48
class BranchWithHooks(BranchConfig):
49
50
    def post_commit(self):
51
        return "bzrlib.ahook bzrlib.ahook"
52
53
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
54
class CapturingReporter(NullCommitReporter):
55
    """This reporter captures the calls made to it for evaluation later."""
56
57
    def __init__(self):
58
        # a list of the calls this received
59
        self.calls = []
60
61
    def snapshot_change(self, change, path):
62
        self.calls.append(('change', change, path))
63
64
    def deleted(self, file_id):
65
        self.calls.append(('deleted', file_id))
66
67
    def missing(self, path):
68
        self.calls.append(('missing', path))
69
70
    def renamed(self, change, old_path, new_path):
71
        self.calls.append(('renamed', change, old_path, new_path))
72
73
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
74
class TestCommit(TestCaseWithTransport):
1390 by Robert Collins
pair programming worx... merge integration and weave
75
1246 by Martin Pool
- add very simple commit tests
76
    def test_simple_commit(self):
77
        """Commit and check two versions of a single file."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
78
        wt = self.make_branch_and_tree('.')
79
        b = wt.branch
1246 by Martin Pool
- add very simple commit tests
80
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
81
        wt.add('hello')
82
        wt.commit(message='add hello')
83
        file_id = wt.path2id('hello')
1246 by Martin Pool
- add very simple commit tests
84
85
        file('hello', 'w').write('version 2')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
86
        wt.commit(message='commit 2')
1246 by Martin Pool
- add very simple commit tests
87
88
        eq = self.assertEquals
89
        eq(b.revno(), 2)
90
        rh = b.revision_history()
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
91
        rev = b.repository.get_revision(rh[0])
1246 by Martin Pool
- add very simple commit tests
92
        eq(rev.message, 'add hello')
93
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
94
        tree1 = b.repository.revision_tree(rh[0])
1246 by Martin Pool
- add very simple commit tests
95
        text = tree1.get_file_text(file_id)
96
        eq(text, 'hello world')
97
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
98
        tree2 = b.repository.revision_tree(rh[1])
1246 by Martin Pool
- add very simple commit tests
99
        eq(tree2.get_file_text(file_id), 'version 2')
100
101
    def test_delete_commit(self):
102
        """Test a commit with a deleted file"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
103
        wt = self.make_branch_and_tree('.')
104
        b = wt.branch
1247 by Martin Pool
- tests for deletion and removal of files in commits
105
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
106
        wt.add(['hello'], ['hello-id'])
107
        wt.commit(message='add hello')
1247 by Martin Pool
- tests for deletion and removal of files in commits
108
109
        os.remove('hello')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
110
        wt.commit('removed hello', rev_id='rev2')
1247 by Martin Pool
- tests for deletion and removal of files in commits
111
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
112
        tree = b.repository.revision_tree('rev2')
1247 by Martin Pool
- tests for deletion and removal of files in commits
113
        self.assertFalse(tree.has_id('hello-id'))
114
1253 by Martin Pool
- test that pointless commits are trapped
115
    def test_pointless_commit(self):
116
        """Commit refuses unless there are changes or it's forced."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
117
        wt = self.make_branch_and_tree('.')
118
        b = wt.branch
1253 by Martin Pool
- test that pointless commits are trapped
119
        file('hello', 'w').write('hello')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
120
        wt.add(['hello'])
121
        wt.commit(message='add hello')
1253 by Martin Pool
- test that pointless commits are trapped
122
        self.assertEquals(b.revno(), 1)
123
        self.assertRaises(PointlessCommit,
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
124
                          wt.commit,
1253 by Martin Pool
- test that pointless commits are trapped
125
                          message='fails',
126
                          allow_pointless=False)
127
        self.assertEquals(b.revno(), 1)
128
        
1252 by Martin Pool
- add test for commit of an empty tree
129
    def test_commit_empty(self):
1253 by Martin Pool
- test that pointless commits are trapped
130
        """Commiting an empty tree works."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
131
        wt = self.make_branch_and_tree('.')
132
        b = wt.branch
133
        wt.commit(message='empty tree', allow_pointless=True)
1253 by Martin Pool
- test that pointless commits are trapped
134
        self.assertRaises(PointlessCommit,
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
135
                          wt.commit,
1253 by Martin Pool
- test that pointless commits are trapped
136
                          message='empty tree',
137
                          allow_pointless=False)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
138
        wt.commit(message='empty tree', allow_pointless=True)
1252 by Martin Pool
- add test for commit of an empty tree
139
        self.assertEquals(b.revno(), 2)
140
141
    def test_selective_delete(self):
142
        """Selective commit in tree with deletions"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
143
        wt = self.make_branch_and_tree('.')
144
        b = wt.branch
1254 by Martin Pool
- fix handling of selective commit with deleted files
145
        file('hello', 'w').write('hello')
146
        file('buongia', 'w').write('buongia')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
147
        wt.add(['hello', 'buongia'],
1255 by Martin Pool
- more tests for selective commit of deletion
148
              ['hello-id', 'buongia-id'])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
149
        wt.commit(message='add files',
1255 by Martin Pool
- more tests for selective commit of deletion
150
                 rev_id='test@rev-1')
1254 by Martin Pool
- fix handling of selective commit with deleted files
151
        
152
        os.remove('hello')
153
        file('buongia', 'w').write('new text')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
154
        wt.commit(message='update text',
1254 by Martin Pool
- fix handling of selective commit with deleted files
155
                 specific_files=['buongia'],
1255 by Martin Pool
- more tests for selective commit of deletion
156
                 allow_pointless=False,
157
                 rev_id='test@rev-2')
1254 by Martin Pool
- fix handling of selective commit with deleted files
158
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
159
        wt.commit(message='remove hello',
1254 by Martin Pool
- fix handling of selective commit with deleted files
160
                 specific_files=['hello'],
1255 by Martin Pool
- more tests for selective commit of deletion
161
                 allow_pointless=False,
162
                 rev_id='test@rev-3')
1254 by Martin Pool
- fix handling of selective commit with deleted files
163
164
        eq = self.assertEquals
165
        eq(b.revno(), 3)
1255 by Martin Pool
- more tests for selective commit of deletion
166
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
167
        tree2 = b.repository.revision_tree('test@rev-2')
1255 by Martin Pool
- more tests for selective commit of deletion
168
        self.assertTrue(tree2.has_filename('hello'))
169
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
170
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
171
        
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
172
        tree3 = b.repository.revision_tree('test@rev-3')
1255 by Martin Pool
- more tests for selective commit of deletion
173
        self.assertFalse(tree3.has_filename('hello'))
174
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
1285 by Martin Pool
- fix bug in committing files that are renamed but not modified
175
176
    def test_commit_rename(self):
177
        """Test commit of a revision where a file is renamed."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
178
        tree = self.make_branch_and_tree('.')
179
        b = tree.branch
1185.38.7 by John Arbash Meinel
Updated build_tree to use fixed line-endings for tests which read the file contents and compare
180
        self.build_tree(['hello'], line_endings='binary')
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
181
        tree.add(['hello'], ['hello-id'])
182
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
1285 by Martin Pool
- fix bug in committing files that are renamed but not modified
183
1508.1.7 by Robert Collins
Move rename_one from Branch to WorkingTree. (Robert Collins).
184
        tree.rename_one('hello', 'fruity')
185
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
1285 by Martin Pool
- fix bug in committing files that are renamed but not modified
186
1303 by Martin Pool
- commit updates entry_version
187
        eq = self.assertEquals
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
188
        tree1 = b.repository.revision_tree('test@rev-1')
1303 by Martin Pool
- commit updates entry_version
189
        eq(tree1.id2path('hello-id'), 'hello')
190
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
1285 by Martin Pool
- fix bug in committing files that are renamed but not modified
191
        self.assertFalse(tree1.has_filename('fruity'))
1291 by Martin Pool
- add test for moving files between directories
192
        self.check_inventory_shape(tree1.inventory, ['hello'])
1303 by Martin Pool
- commit updates entry_version
193
        ie = tree1.inventory['hello-id']
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
194
        eq(ie.revision, 'test@rev-1')
1285 by Martin Pool
- fix bug in committing files that are renamed but not modified
195
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
196
        tree2 = b.repository.revision_tree('test@rev-2')
1303 by Martin Pool
- commit updates entry_version
197
        eq(tree2.id2path('hello-id'), 'fruity')
198
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
1291 by Martin Pool
- add test for moving files between directories
199
        self.check_inventory_shape(tree2.inventory, ['fruity'])
1303 by Martin Pool
- commit updates entry_version
200
        ie = tree2.inventory['hello-id']
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
201
        eq(ie.revision, 'test@rev-2')
1291 by Martin Pool
- add test for moving files between directories
202
203
    def test_reused_rev_id(self):
1292 by Martin Pool
- add check that revision ids cannot be committed twice
204
        """Test that a revision id cannot be reused in a branch"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
205
        wt = self.make_branch_and_tree('.')
206
        b = wt.branch
207
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
1292 by Martin Pool
- add check that revision ids cannot be committed twice
208
        self.assertRaises(Exception,
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
209
                          wt.commit,
1292 by Martin Pool
- add check that revision ids cannot be committed twice
210
                          message='reused id',
211
                          rev_id='test@rev-1',
212
                          allow_pointless=True)
1291 by Martin Pool
- add test for moving files between directories
213
214
    def test_commit_move(self):
215
        """Test commit of revisions with moved files and directories"""
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
216
        eq = self.assertEquals
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
217
        wt = self.make_branch_and_tree('.')
218
        b = wt.branch
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
219
        r1 = 'test@rev-1'
1291 by Martin Pool
- add test for moving files between directories
220
        self.build_tree(['hello', 'a/', 'b/'])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
221
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
222
        wt.commit('initial', rev_id=r1, allow_pointless=False)
223
        wt.move(['hello'], 'a')
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
224
        r2 = 'test@rev-2'
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
225
        wt.commit('two', rev_id=r2, allow_pointless=False)
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
226
        self.check_inventory_shape(wt.read_working_inventory(),
1291 by Martin Pool
- add test for moving files between directories
227
                                   ['a', 'a/hello', 'b'])
228
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
229
        wt.move(['b'], 'a')
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
230
        r3 = 'test@rev-3'
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
231
        wt.commit('three', rev_id=r3, allow_pointless=False)
232
        self.check_inventory_shape(wt.read_working_inventory(),
1291 by Martin Pool
- add test for moving files between directories
233
                                   ['a', 'a/hello', 'a/b'])
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
234
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
1291 by Martin Pool
- add test for moving files between directories
235
                                   ['a', 'a/hello', 'a/b'])
236
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
237
        wt.move(['a/hello'], 'a/b')
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
238
        r4 = 'test@rev-4'
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
239
        wt.commit('four', rev_id=r4, allow_pointless=False)
240
        self.check_inventory_shape(wt.read_working_inventory(),
1291 by Martin Pool
- add test for moving files between directories
241
                                   ['a', 'a/b/hello', 'a/b'])
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
242
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
243
        inv = b.repository.get_revision_inventory(r4)
1092.2.21 by Robert Collins
convert name_version to revision in inventory entries
244
        eq(inv['hello-id'].revision, r4)
245
        eq(inv['a-id'].revision, r1)
246
        eq(inv['b-id'].revision, r3)
1306 by Martin Pool
- tests that name_version is updated properly in renames/moves
247
        
1246 by Martin Pool
- add very simple commit tests
248
    def test_removed_commit(self):
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
249
        """Commit with a removed file"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
250
        wt = self.make_branch_and_tree('.')
251
        b = wt.branch
1247 by Martin Pool
- tests for deletion and removal of files in commits
252
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
253
        wt.add(['hello'], ['hello-id'])
254
        wt.commit(message='add hello')
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
255
        wt.remove('hello')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
256
        wt.commit('removed hello', rev_id='rev2')
1247 by Martin Pool
- tests for deletion and removal of files in commits
257
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
258
        tree = b.repository.revision_tree('rev2')
1247 by Martin Pool
- tests for deletion and removal of files in commits
259
        self.assertFalse(tree.has_id('hello-id'))
1246 by Martin Pool
- add very simple commit tests
260
1256 by Martin Pool
- test that commits append to the tree's ancestry
261
    def test_committed_ancestry(self):
262
        """Test commit appends revisions to ancestry."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
263
        wt = self.make_branch_and_tree('.')
264
        b = wt.branch
1256 by Martin Pool
- test that commits append to the tree's ancestry
265
        rev_ids = []
266
        for i in range(4):
267
            file('hello', 'w').write((str(i) * 4) + '\n')
268
            if i == 0:
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
269
                wt.add(['hello'], ['hello-id'])
1256 by Martin Pool
- test that commits append to the tree's ancestry
270
            rev_id = 'test@rev-%d' % (i+1)
271
            rev_ids.append(rev_id)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
272
            wt.commit(message='rev %d' % (i+1),
1256 by Martin Pool
- test that commits append to the tree's ancestry
273
                     rev_id=rev_id)
274
        eq = self.assertEquals
275
        eq(b.revision_history(), rev_ids)
276
        for i in range(4):
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
277
            anc = b.repository.get_ancestry(rev_ids[i])
1390 by Robert Collins
pair programming worx... merge integration and weave
278
            eq(anc, [None] + rev_ids[:i+1])
1416 by Robert Collins
when committing a specific file, include all its parents
279
280
    def test_commit_new_subdir_child_selective(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
281
        wt = self.make_branch_and_tree('.')
282
        b = wt.branch
1416 by Robert Collins
when committing a specific file, include all its parents
283
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
284
        wt.add(['dir', 'dir/file1', 'dir/file2'],
1416 by Robert Collins
when committing a specific file, include all its parents
285
              ['dirid', 'file1id', 'file2id'])
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
286
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
287
        inv = b.repository.get_inventory('1')
1416 by Robert Collins
when committing a specific file, include all its parents
288
        self.assertEqual('1', inv['dirid'].revision)
289
        self.assertEqual('1', inv['file1id'].revision)
290
        # FIXME: This should raise a KeyError I think, rbc20051006
291
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
1185.16.65 by mbp at sourcefrog
- new commit --strict option
292
293
    def test_strict_commit(self):
294
        """Try and commit with unknown files and strict = True, should fail."""
295
        from bzrlib.errors import StrictCommitFailed
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
296
        wt = self.make_branch_and_tree('.')
297
        b = wt.branch
1185.16.65 by mbp at sourcefrog
- new commit --strict option
298
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
299
        wt.add('hello')
1185.16.65 by mbp at sourcefrog
- new commit --strict option
300
        file('goodbye', 'w').write('goodbye cruel world!')
1534.4.35 by Robert Collins
Give branch its own basis tree and last_revision methods; deprecated branch.working_tree()
301
        self.assertRaises(StrictCommitFailed, wt.commit,
1185.16.65 by mbp at sourcefrog
- new commit --strict option
302
            message='add hello but not goodbye', strict=True)
303
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
304
    def test_strict_commit_without_unknowns(self):
305
        """Try and commit with no unknown files and strict = True,
306
        should work."""
307
        from bzrlib.errors import StrictCommitFailed
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
308
        wt = self.make_branch_and_tree('.')
309
        b = wt.branch
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
310
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
311
        wt.add('hello')
312
        wt.commit(message='add hello', strict=True)
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
313
1185.16.65 by mbp at sourcefrog
- new commit --strict option
314
    def test_nonstrict_commit(self):
315
        """Try and commit with unknown files and strict = False, should work."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
316
        wt = self.make_branch_and_tree('.')
317
        b = wt.branch
1185.16.65 by mbp at sourcefrog
- new commit --strict option
318
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
319
        wt.add('hello')
1185.16.65 by mbp at sourcefrog
- new commit --strict option
320
        file('goodbye', 'w').write('goodbye cruel world!')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
321
        wt.commit(message='add hello but not goodbye', strict=False)
1185.16.72 by Martin Pool
[merge] from robert and fix up tests
322
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
323
    def test_nonstrict_commit_without_unknowns(self):
324
        """Try and commit with no unknown files and strict = False,
325
        should work."""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
326
        wt = self.make_branch_and_tree('.')
327
        b = wt.branch
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
328
        file('hello', 'w').write('hello world')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
329
        wt.add('hello')
330
        wt.commit(message='add hello', strict=False)
1185.22.4 by Michael Ellerman
Strict commit was a little .. ah .. too strict, oops :}
331
1442.1.60 by Robert Collins
gpg sign commits if the policy says we need to
332
    def test_signed_commit(self):
333
        import bzrlib.gpg
334
        import bzrlib.commit as commit
335
        oldstrategy = bzrlib.gpg.GPGStrategy
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
336
        wt = self.make_branch_and_tree('.')
337
        branch = wt.branch
338
        wt.commit("base", allow_pointless=True, rev_id='A')
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
339
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1442.1.60 by Robert Collins
gpg sign commits if the policy says we need to
340
        try:
341
            from bzrlib.testament import Testament
342
            # monkey patch gpg signing mechanism
343
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
2149.1.3 by Aaron Bentley
Updates from review comments
344
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
345
                                                      allow_pointless=True,
346
                                                      rev_id='B',
347
                                                      working_tree=wt)
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
348
            self.assertEqual(Testament.from_revision(branch.repository,
1185.65.1 by Aaron Bentley
Refactored out ControlFiles and RevisionStore from _Branch
349
                             'B').as_short_text(),
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
350
                             branch.repository.get_signature_text('B'))
1442.1.60 by Robert Collins
gpg sign commits if the policy says we need to
351
        finally:
352
            bzrlib.gpg.GPGStrategy = oldstrategy
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
353
354
    def test_commit_failed_signature(self):
355
        import bzrlib.gpg
356
        import bzrlib.commit as commit
357
        oldstrategy = bzrlib.gpg.GPGStrategy
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
358
        wt = self.make_branch_and_tree('.')
359
        branch = wt.branch
360
        wt.commit("base", allow_pointless=True, rev_id='A')
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
361
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
362
        try:
363
            from bzrlib.testament import Testament
364
            # monkey patch gpg signing mechanism
365
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
366
            config = MustSignConfig(branch)
367
            self.assertRaises(SigningFailed,
368
                              commit.Commit(config=config).commit,
1534.4.34 by Robert Collins
Fix remaining uses of deprecated apis within bzrlib.
369
                              message="base",
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
370
                              allow_pointless=True,
1534.4.34 by Robert Collins
Fix remaining uses of deprecated apis within bzrlib.
371
                              rev_id='B',
372
                              working_tree=wt)
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
373
            branch = Branch.open(self.get_url('.'))
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
374
            self.assertEqual(branch.revision_history(), ['A'])
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
375
            self.failIf(branch.repository.has_revision('B'))
1442.1.62 by Robert Collins
Allow creation of testaments from uncommitted data, and use that to get signatures before committing revisions.
376
        finally:
377
            bzrlib.gpg.GPGStrategy = oldstrategy
1472 by Robert Collins
post commit hook, first pass implementation
378
379
    def test_commit_invokes_hooks(self):
380
        import bzrlib.commit as commit
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
381
        wt = self.make_branch_and_tree('.')
382
        branch = wt.branch
1472 by Robert Collins
post commit hook, first pass implementation
383
        calls = []
384
        def called(branch, rev_id):
385
            calls.append('called')
386
        bzrlib.ahook = called
387
        try:
388
            config = BranchWithHooks(branch)
389
            commit.Commit(config=config).commit(
2149.1.3 by Aaron Bentley
Updates from review comments
390
                            message = "base",
1472 by Robert Collins
post commit hook, first pass implementation
391
                            allow_pointless=True,
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
392
                            rev_id='A', working_tree = wt)
1472 by Robert Collins
post commit hook, first pass implementation
393
            self.assertEqual(['called', 'called'], calls)
394
        finally:
395
            del bzrlib.ahook
1593.1.1 by Robert Collins
Move responsibility for setting branch nickname in commits to the WorkingTree convenience function.
396
397
    def test_commit_object_doesnt_set_nick(self):
398
        # using the Commit object directly does not set the branch nick.
399
        wt = self.make_branch_and_tree('.')
400
        c = Commit()
2149.1.3 by Aaron Bentley
Updates from review comments
401
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
1593.1.1 by Robert Collins
Move responsibility for setting branch nickname in commits to the WorkingTree convenience function.
402
        self.assertEquals(wt.branch.revno(), 1)
403
        self.assertEqual({},
404
                         wt.branch.repository.get_revision(
405
                            wt.branch.last_revision()).properties)
406
1614.1.1 by Aaron Bentley
Fixed master locking in commit
407
    def test_safe_master_lock(self):
408
        os.mkdir('master')
409
        master = BzrDirMetaFormat1().initialize('master')
410
        master.create_repository()
411
        master_branch = master.create_branch()
412
        master.create_workingtree()
413
        bound = master.sprout('bound')
414
        wt = bound.open_workingtree()
415
        wt.branch.set_bound_location(os.path.realpath('master'))
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
416
417
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
1614.1.1 by Aaron Bentley
Fixed master locking in commit
418
        master_branch.lock_write()
1658.1.5 by Martin Pool
Release more locks taken during test suite
419
        try:
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
420
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
1658.1.5 by Martin Pool
Release more locks taken during test suite
421
            self.assertRaises(LockContention, wt.commit, 'silly')
422
        finally:
1957.1.17 by John Arbash Meinel
Change tests that expect locking to fail to timeout sooner.
423
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
1658.1.5 by Martin Pool
Release more locks taken during test suite
424
            master_branch.unlock()
1668.1.3 by Martin Pool
[patch] use the correct transaction when committing snapshot (Malone: #43959)
425
426
    def test_commit_bound_merge(self):
427
        # see bug #43959; commit of a merge in a bound branch fails to push
428
        # the new commit into the master
429
        master_branch = self.make_branch('master')
430
        bound_tree = self.make_branch_and_tree('bound')
431
        bound_tree.branch.bind(master_branch)
432
433
        self.build_tree_contents([('bound/content_file', 'initial contents\n')])
434
        bound_tree.add(['content_file'])
435
        bound_tree.commit(message='woo!')
436
437
        other_bzrdir = master_branch.bzrdir.sprout('other')
438
        other_tree = other_bzrdir.open_workingtree()
439
440
        # do a commit to the the other branch changing the content file so
441
        # that our commit after merging will have a merged revision in the
442
        # content file history.
443
        self.build_tree_contents([('other/content_file', 'change in other\n')])
444
        other_tree.commit('change in other')
445
446
        # do a merge into the bound branch from other, and then change the
447
        # content file locally to force a new revision (rather than using the
448
        # revision from other). This forces extra processing in commit.
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
449
        bound_tree.merge_from_branch(other_tree.branch)
1668.1.3 by Martin Pool
[patch] use the correct transaction when committing snapshot (Malone: #43959)
450
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
451
452
        # before #34959 was fixed, this failed with 'revision not present in
453
        # weave' when trying to implicitly push from the bound branch to the master
454
        bound_tree.commit(message='commit of merge in bound tree')
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
455
456
    def test_commit_reporting_after_merge(self):
457
        # when doing a commit of a merge, the reporter needs to still 
458
        # be called for each item that is added/removed/deleted.
459
        this_tree = self.make_branch_and_tree('this')
460
        # we need a bunch of files and dirs, to perform one action on each.
461
        self.build_tree([
462
            'this/dirtorename/',
463
            'this/dirtoreparent/',
464
            'this/dirtoleave/',
465
            'this/dirtoremove/',
466
            'this/filetoreparent',
467
            'this/filetorename',
468
            'this/filetomodify',
469
            'this/filetoremove',
470
            'this/filetoleave']
471
            )
472
        this_tree.add([
473
            'dirtorename',
474
            'dirtoreparent',
475
            'dirtoleave',
476
            'dirtoremove',
477
            'filetoreparent',
478
            'filetorename',
479
            'filetomodify',
480
            'filetoremove',
481
            'filetoleave']
482
            )
483
        this_tree.commit('create_files')
484
        other_dir = this_tree.bzrdir.sprout('other')
485
        other_tree = other_dir.open_workingtree()
486
        other_tree.lock_write()
487
        # perform the needed actions on the files and dirs.
488
        try:
489
            other_tree.rename_one('dirtorename', 'renameddir')
490
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
491
            other_tree.rename_one('filetorename', 'renamedfile')
492
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
493
            other_tree.remove(['dirtoremove', 'filetoremove'])
494
            self.build_tree_contents([
495
                ('other/newdir/', ),
496
                ('other/filetomodify', 'new content'),
497
                ('other/newfile', 'new file content')])
498
            other_tree.add('newfile')
499
            other_tree.add('newdir/')
500
            other_tree.commit('modify all sample files and dirs.')
501
        finally:
502
            other_tree.unlock()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
503
        this_tree.merge_from_branch(other_tree.branch)
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
504
        reporter = CapturingReporter()
505
        this_tree.commit('do the commit', reporter=reporter)
506
        self.assertEqual([
1910.2.3 by Aaron Bentley
All tests pass
507
            ('change', 'unchanged', ''),
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
508
            ('change', 'unchanged', 'dirtoleave'),
509
            ('change', 'unchanged', 'filetoleave'),
510
            ('change', 'modified', 'filetomodify'),
511
            ('change', 'added', 'newdir'),
512
            ('change', 'added', 'newfile'),
513
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
514
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
515
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
516
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
517
            ('deleted', 'dirtoremove'),
518
            ('deleted', 'filetoremove'),
519
            ],
520
            reporter.calls)
1551.7.24 by Aaron Bentley
Ensure commit respects file spec when committing removals
521
522
    def test_commit_removals_respects_filespec(self):
523
        """Commit respects the specified_files for removals."""
524
        tree = self.make_branch_and_tree('.')
525
        self.build_tree(['a', 'b'])
526
        tree.add(['a', 'b'])
527
        tree.commit('added a, b')
528
        tree.remove(['a', 'b'])
1906.1.1 by Robert Collins
(robertc) Trivial change to test_commit_removals_respects_filespec to be easir to read.
529
        tree.commit('removed a', specific_files='a')
1551.7.24 by Aaron Bentley
Ensure commit respects file spec when committing removals
530
        basis = tree.basis_tree().inventory
531
        self.assertIs(None, basis.path2id('a'))
532
        self.assertFalse(basis.path2id('b') is None)
1864.2.1 by John Arbash Meinel
Commit timestamp restricted to 1ms precision.
533
534
    def test_commit_saves_1ms_timestamp(self):
535
        """Passing in a timestamp is saved with 1ms resolution"""
536
        tree = self.make_branch_and_tree('.')
537
        self.build_tree(['a'])
538
        tree.add('a')
539
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
540
                    rev_id='a1')
541
542
        rev = tree.branch.repository.get_revision('a1')
543
        self.assertEqual(1153248633.419, rev.timestamp)
544
545
    def test_commit_has_1ms_resolution(self):
546
        """Allowing commit to generate the timestamp also has 1ms resolution"""
547
        tree = self.make_branch_and_tree('.')
548
        self.build_tree(['a'])
549
        tree.add('a')
550
        tree.commit('added a', rev_id='a1')
551
552
        rev = tree.branch.repository.get_revision('a1')
553
        timestamp = rev.timestamp
554
        timestamp_1ms = round(timestamp, 3)
555
        self.assertEqual(timestamp_1ms, timestamp)
1959.4.1 by Aaron Bentley
Correctly handle all file kind changes
556
557
    def test_commit_kind_changes(self):
558
        if not osutils.has_symlinks():
559
            raise tests.TestSkipped('Test requires symlink support')
560
        tree = self.make_branch_and_tree('.')
561
        os.symlink('target', 'name')
562
        tree.add('name', 'a-file-id')
563
        tree.commit('Added a symlink')
564
        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
565
566
        os.unlink('name')
567
        self.build_tree(['name'])
568
        tree.commit('Changed symlink to file')
569
        self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
570
571
        os.unlink('name')
572
        os.symlink('target', 'name')
573
        tree.commit('file to symlink')
574
        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
575
576
        os.unlink('name')
577
        os.mkdir('name')
578
        tree.commit('symlink to directory')
579
        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
580
581
        os.rmdir('name')
582
        os.symlink('target', 'name')
583
        tree.commit('directory to symlink')
584
        self.assertEqual('symlink', tree.basis_tree().kind('a-file-id'))
585
586
        # prepare for directory <-> file tests
587
        os.unlink('name')
588
        os.mkdir('name')
589
        tree.commit('symlink to directory')
590
        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
591
592
        os.rmdir('name')
593
        self.build_tree(['name'])
594
        tree.commit('Changed directory to file')
595
        self.assertEqual('file', tree.basis_tree().kind('a-file-id'))
596
597
        os.unlink('name')
598
        os.mkdir('name')
599
        tree.commit('file to directory')
600
        self.assertEqual('directory', tree.basis_tree().kind('a-file-id'))
1959.4.2 by Aaron Bentley
Merge bzr.dev
601
1551.8.29 by Aaron Bentley
Stop accepting non-existant files in commit (#50793)
602
    def test_commit_unversioned_specified(self):
603
        """Commit should raise if specified files isn't in basis or worktree"""
604
        tree = self.make_branch_and_tree('.')
605
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
606
                          'message', specific_files=['bogus'])
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
607
608
    class Callback(object):
609
        
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
610
        def __init__(self, message, testcase):
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
611
            self.called = False
612
            self.message = message
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
613
            self.testcase = testcase
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
614
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
615
        def __call__(self, commit_obj):
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
616
            self.called = True
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
617
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
618
            return self.message
619
620
    def test_commit_callback(self):
621
        """Commit should invoke a callback to get the message"""
622
623
        tree = self.make_branch_and_tree('.')
624
        try:
625
            tree.commit()
626
        except Exception, e:
627
            self.assertTrue(isinstance(e, BzrError))
2149.1.3 by Aaron Bentley
Updates from review comments
628
            self.assertEqual('The message or message_callback keyword'
629
                             ' parameter is required for commit().', str(e))
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
630
        else:
631
            self.fail('exception not raised')
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
632
        cb = self.Callback(u'commit 1', self)
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
633
        tree.commit(message_callback=cb)
634
        self.assertTrue(cb.called)
635
        repository = tree.branch.repository
636
        message = repository.get_revision(tree.last_revision()).message
637
        self.assertEqual('commit 1', message)
638
639
    def test_no_callback_pointless(self):
640
        """Callback should not be invoked for pointless commit"""
641
        tree = self.make_branch_and_tree('.')
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
642
        cb = self.Callback(u'commit 2', self)
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
643
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
644
                          allow_pointless=False)
645
        self.assertFalse(cb.called)
646
647
    def test_no_callback_netfailure(self):
648
        """Callback should not be invoked if connectivity fails"""
2149.1.3 by Aaron Bentley
Updates from review comments
649
        tree = self.make_branch_and_tree('.')
2149.1.4 by Aaron Bentley
Add additional test that callback is called with a Commit instance
650
        cb = self.Callback(u'commit 2', self)
2149.1.3 by Aaron Bentley
Updates from review comments
651
        repository = tree.branch.repository
2149.1.1 by Aaron Bentley
Provide a message_callback parameter to tree.commit
652
        # simulate network failure
653
        def raise_(self, arg, arg2):
654
            raise errors.NoSuchFile('foo')
655
        repository.add_inventory = raise_
656
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
657
        self.assertFalse(cb.called)