~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

(mbp) merge bzr.dev to 0.8, prepare for release

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import os
19
19
 
20
 
from bzrlib.selftest import TestCaseInTempDir
 
20
import bzrlib
 
21
from bzrlib.tests import TestCaseWithTransport
21
22
from bzrlib.branch import Branch
 
23
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
22
24
from bzrlib.workingtree import WorkingTree
23
25
from bzrlib.commit import Commit
24
26
from bzrlib.config import BranchConfig
25
 
from bzrlib.errors import PointlessCommit, BzrError, SigningFailed
 
27
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
 
28
                           LockContention)
26
29
 
27
30
 
28
31
# TODO: Test commit with some added, and added-but-missing files
36
39
        return ['cat', '-']
37
40
 
38
41
 
39
 
class TestCommit(TestCaseInTempDir):
 
42
class BranchWithHooks(BranchConfig):
 
43
 
 
44
    def post_commit(self):
 
45
        return "bzrlib.ahook bzrlib.ahook"
 
46
 
 
47
 
 
48
class TestCommit(TestCaseWithTransport):
40
49
 
41
50
    def test_simple_commit(self):
42
51
        """Commit and check two versions of a single file."""
43
 
        b = Branch.initialize('.')
 
52
        wt = self.make_branch_and_tree('.')
 
53
        b = wt.branch
44
54
        file('hello', 'w').write('hello world')
45
 
        b.add('hello')
46
 
        b.commit(message='add hello')
47
 
        file_id = b.working_tree().path2id('hello')
 
55
        wt.add('hello')
 
56
        wt.commit(message='add hello')
 
57
        file_id = wt.path2id('hello')
48
58
 
49
59
        file('hello', 'w').write('version 2')
50
 
        b.commit(message='commit 2')
 
60
        wt.commit(message='commit 2')
51
61
 
52
62
        eq = self.assertEquals
53
63
        eq(b.revno(), 2)
54
64
        rh = b.revision_history()
55
 
        rev = b.get_revision(rh[0])
 
65
        rev = b.repository.get_revision(rh[0])
56
66
        eq(rev.message, 'add hello')
57
67
 
58
 
        tree1 = b.revision_tree(rh[0])
 
68
        tree1 = b.repository.revision_tree(rh[0])
59
69
        text = tree1.get_file_text(file_id)
60
70
        eq(text, 'hello world')
61
71
 
62
 
        tree2 = b.revision_tree(rh[1])
 
72
        tree2 = b.repository.revision_tree(rh[1])
63
73
        eq(tree2.get_file_text(file_id), 'version 2')
64
74
 
65
75
    def test_delete_commit(self):
66
76
        """Test a commit with a deleted file"""
67
 
        b = Branch.initialize('.')
 
77
        wt = self.make_branch_and_tree('.')
 
78
        b = wt.branch
68
79
        file('hello', 'w').write('hello world')
69
 
        b.add(['hello'], ['hello-id'])
70
 
        b.commit(message='add hello')
 
80
        wt.add(['hello'], ['hello-id'])
 
81
        wt.commit(message='add hello')
71
82
 
72
83
        os.remove('hello')
73
 
        b.commit('removed hello', rev_id='rev2')
 
84
        wt.commit('removed hello', rev_id='rev2')
74
85
 
75
 
        tree = b.revision_tree('rev2')
 
86
        tree = b.repository.revision_tree('rev2')
76
87
        self.assertFalse(tree.has_id('hello-id'))
77
88
 
78
 
 
79
89
    def test_pointless_commit(self):
80
90
        """Commit refuses unless there are changes or it's forced."""
81
 
        b = Branch.initialize('.')
 
91
        wt = self.make_branch_and_tree('.')
 
92
        b = wt.branch
82
93
        file('hello', 'w').write('hello')
83
 
        b.add(['hello'])
84
 
        b.commit(message='add hello')
 
94
        wt.add(['hello'])
 
95
        wt.commit(message='add hello')
85
96
        self.assertEquals(b.revno(), 1)
86
97
        self.assertRaises(PointlessCommit,
87
 
                          b.commit,
 
98
                          wt.commit,
88
99
                          message='fails',
89
100
                          allow_pointless=False)
90
101
        self.assertEquals(b.revno(), 1)
91
102
        
92
 
 
93
 
 
94
103
    def test_commit_empty(self):
95
104
        """Commiting an empty tree works."""
96
 
        b = Branch.initialize('.')
97
 
        b.commit(message='empty tree', allow_pointless=True)
 
105
        wt = self.make_branch_and_tree('.')
 
106
        b = wt.branch
 
107
        wt.commit(message='empty tree', allow_pointless=True)
98
108
        self.assertRaises(PointlessCommit,
99
 
                          b.commit,
 
109
                          wt.commit,
100
110
                          message='empty tree',
101
111
                          allow_pointless=False)
102
 
        b.commit(message='empty tree', allow_pointless=True)
 
112
        wt.commit(message='empty tree', allow_pointless=True)
103
113
        self.assertEquals(b.revno(), 2)
104
114
 
105
 
 
106
115
    def test_selective_delete(self):
107
116
        """Selective commit in tree with deletions"""
108
 
        b = Branch.initialize('.')
 
117
        wt = self.make_branch_and_tree('.')
 
118
        b = wt.branch
109
119
        file('hello', 'w').write('hello')
110
120
        file('buongia', 'w').write('buongia')
111
 
        b.add(['hello', 'buongia'],
 
121
        wt.add(['hello', 'buongia'],
112
122
              ['hello-id', 'buongia-id'])
113
 
        b.commit(message='add files',
 
123
        wt.commit(message='add files',
114
124
                 rev_id='test@rev-1')
115
125
        
116
126
        os.remove('hello')
117
127
        file('buongia', 'w').write('new text')
118
 
        b.commit(message='update text',
 
128
        wt.commit(message='update text',
119
129
                 specific_files=['buongia'],
120
130
                 allow_pointless=False,
121
131
                 rev_id='test@rev-2')
122
132
 
123
 
        b.commit(message='remove hello',
 
133
        wt.commit(message='remove hello',
124
134
                 specific_files=['hello'],
125
135
                 allow_pointless=False,
126
136
                 rev_id='test@rev-3')
128
138
        eq = self.assertEquals
129
139
        eq(b.revno(), 3)
130
140
 
131
 
        tree2 = b.revision_tree('test@rev-2')
 
141
        tree2 = b.repository.revision_tree('test@rev-2')
132
142
        self.assertTrue(tree2.has_filename('hello'))
133
143
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
134
144
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
135
145
        
136
 
        tree3 = b.revision_tree('test@rev-3')
 
146
        tree3 = b.repository.revision_tree('test@rev-3')
137
147
        self.assertFalse(tree3.has_filename('hello'))
138
148
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
139
149
 
140
 
 
141
150
    def test_commit_rename(self):
142
151
        """Test commit of a revision where a file is renamed."""
143
 
        b = Branch.initialize('.')
144
 
        self.build_tree(['hello'])
145
 
        b.add(['hello'], ['hello-id'])
146
 
        b.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
152
        tree = self.make_branch_and_tree('.')
 
153
        b = tree.branch
 
154
        self.build_tree(['hello'], line_endings='binary')
 
155
        tree.add(['hello'], ['hello-id'])
 
156
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
147
157
 
148
 
        b.rename_one('hello', 'fruity')
149
 
        b.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
158
        tree.rename_one('hello', 'fruity')
 
159
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
150
160
 
151
161
        eq = self.assertEquals
152
 
        tree1 = b.revision_tree('test@rev-1')
 
162
        tree1 = b.repository.revision_tree('test@rev-1')
153
163
        eq(tree1.id2path('hello-id'), 'hello')
154
164
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
155
165
        self.assertFalse(tree1.has_filename('fruity'))
157
167
        ie = tree1.inventory['hello-id']
158
168
        eq(ie.revision, 'test@rev-1')
159
169
 
160
 
        tree2 = b.revision_tree('test@rev-2')
 
170
        tree2 = b.repository.revision_tree('test@rev-2')
161
171
        eq(tree2.id2path('hello-id'), 'fruity')
162
172
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
163
173
        self.check_inventory_shape(tree2.inventory, ['fruity'])
164
174
        ie = tree2.inventory['hello-id']
165
175
        eq(ie.revision, 'test@rev-2')
166
176
 
167
 
 
168
177
    def test_reused_rev_id(self):
169
178
        """Test that a revision id cannot be reused in a branch"""
170
 
        b = Branch.initialize('.')
171
 
        b.commit('initial', rev_id='test@rev-1', allow_pointless=True)
 
179
        wt = self.make_branch_and_tree('.')
 
180
        b = wt.branch
 
181
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
172
182
        self.assertRaises(Exception,
173
 
                          b.commit,
 
183
                          wt.commit,
174
184
                          message='reused id',
175
185
                          rev_id='test@rev-1',
176
186
                          allow_pointless=True)
177
 
                          
178
 
 
179
187
 
180
188
    def test_commit_move(self):
181
189
        """Test commit of revisions with moved files and directories"""
182
190
        eq = self.assertEquals
183
 
        b = Branch.initialize('.')
 
191
        wt = self.make_branch_and_tree('.')
 
192
        b = wt.branch
184
193
        r1 = 'test@rev-1'
185
194
        self.build_tree(['hello', 'a/', 'b/'])
186
 
        b.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
187
 
        b.commit('initial', rev_id=r1, allow_pointless=False)
188
 
 
189
 
        b.move(['hello'], 'a')
 
195
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
 
196
        wt.commit('initial', rev_id=r1, allow_pointless=False)
 
197
        wt.move(['hello'], 'a')
190
198
        r2 = 'test@rev-2'
191
 
        b.commit('two', rev_id=r2, allow_pointless=False)
192
 
        self.check_inventory_shape(b.inventory,
 
199
        wt.commit('two', rev_id=r2, allow_pointless=False)
 
200
        self.check_inventory_shape(wt.read_working_inventory(),
193
201
                                   ['a', 'a/hello', 'b'])
194
202
 
195
 
        b.move(['b'], 'a')
 
203
        wt.move(['b'], 'a')
196
204
        r3 = 'test@rev-3'
197
 
        b.commit('three', rev_id=r3, allow_pointless=False)
198
 
        self.check_inventory_shape(b.inventory,
 
205
        wt.commit('three', rev_id=r3, allow_pointless=False)
 
206
        self.check_inventory_shape(wt.read_working_inventory(),
199
207
                                   ['a', 'a/hello', 'a/b'])
200
 
        self.check_inventory_shape(b.get_revision_inventory(r3),
 
208
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
201
209
                                   ['a', 'a/hello', 'a/b'])
202
210
 
203
 
        b.move([os.sep.join(['a', 'hello'])],
204
 
               os.sep.join(['a', 'b']))
 
211
        wt.move(['a/hello'], 'a/b')
205
212
        r4 = 'test@rev-4'
206
 
        b.commit('four', rev_id=r4, allow_pointless=False)
207
 
        self.check_inventory_shape(b.inventory,
 
213
        wt.commit('four', rev_id=r4, allow_pointless=False)
 
214
        self.check_inventory_shape(wt.read_working_inventory(),
208
215
                                   ['a', 'a/b/hello', 'a/b'])
209
216
 
210
 
        inv = b.get_revision_inventory(r4)
 
217
        inv = b.repository.get_revision_inventory(r4)
211
218
        eq(inv['hello-id'].revision, r4)
212
219
        eq(inv['a-id'].revision, r1)
213
220
        eq(inv['b-id'].revision, r3)
214
 
 
215
221
        
216
222
    def test_removed_commit(self):
217
223
        """Commit with a removed file"""
218
 
        b = Branch.initialize('.')
219
 
        wt = b.working_tree()
 
224
        wt = self.make_branch_and_tree('.')
 
225
        b = wt.branch
220
226
        file('hello', 'w').write('hello world')
221
 
        b.add(['hello'], ['hello-id'])
222
 
        b.commit(message='add hello')
223
 
 
224
 
        wt = b.working_tree()  # FIXME: kludge for aliasing of working inventory
 
227
        wt.add(['hello'], ['hello-id'])
 
228
        wt.commit(message='add hello')
225
229
        wt.remove('hello')
226
 
        b.commit('removed hello', rev_id='rev2')
 
230
        wt.commit('removed hello', rev_id='rev2')
227
231
 
228
 
        tree = b.revision_tree('rev2')
 
232
        tree = b.repository.revision_tree('rev2')
229
233
        self.assertFalse(tree.has_id('hello-id'))
230
234
 
231
 
 
232
235
    def test_committed_ancestry(self):
233
236
        """Test commit appends revisions to ancestry."""
234
 
        b = Branch.initialize('.')
 
237
        wt = self.make_branch_and_tree('.')
 
238
        b = wt.branch
235
239
        rev_ids = []
236
240
        for i in range(4):
237
241
            file('hello', 'w').write((str(i) * 4) + '\n')
238
242
            if i == 0:
239
 
                b.add(['hello'], ['hello-id'])
 
243
                wt.add(['hello'], ['hello-id'])
240
244
            rev_id = 'test@rev-%d' % (i+1)
241
245
            rev_ids.append(rev_id)
242
 
            b.commit(message='rev %d' % (i+1),
 
246
            wt.commit(message='rev %d' % (i+1),
243
247
                     rev_id=rev_id)
244
248
        eq = self.assertEquals
245
249
        eq(b.revision_history(), rev_ids)
246
250
        for i in range(4):
247
 
            anc = b.get_ancestry(rev_ids[i])
 
251
            anc = b.repository.get_ancestry(rev_ids[i])
248
252
            eq(anc, [None] + rev_ids[:i+1])
249
253
 
250
254
    def test_commit_new_subdir_child_selective(self):
251
 
        b = Branch.initialize('.')
 
255
        wt = self.make_branch_and_tree('.')
 
256
        b = wt.branch
252
257
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
253
 
        b.add(['dir', 'dir/file1', 'dir/file2'],
 
258
        wt.add(['dir', 'dir/file1', 'dir/file2'],
254
259
              ['dirid', 'file1id', 'file2id'])
255
 
        b.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
256
 
        inv = b.get_inventory('1')
 
260
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
 
261
        inv = b.repository.get_inventory('1')
257
262
        self.assertEqual('1', inv['dirid'].revision)
258
263
        self.assertEqual('1', inv['file1id'].revision)
259
264
        # FIXME: This should raise a KeyError I think, rbc20051006
262
267
    def test_strict_commit(self):
263
268
        """Try and commit with unknown files and strict = True, should fail."""
264
269
        from bzrlib.errors import StrictCommitFailed
265
 
        b = Branch.initialize('.')
 
270
        wt = self.make_branch_and_tree('.')
 
271
        b = wt.branch
266
272
        file('hello', 'w').write('hello world')
267
 
        b.add('hello')
 
273
        wt.add('hello')
268
274
        file('goodbye', 'w').write('goodbye cruel world!')
269
 
        self.assertRaises(StrictCommitFailed, b.commit,
 
275
        self.assertRaises(StrictCommitFailed, wt.commit,
270
276
            message='add hello but not goodbye', strict=True)
271
277
 
 
278
    def test_strict_commit_without_unknowns(self):
 
279
        """Try and commit with no unknown files and strict = True,
 
280
        should work."""
 
281
        from bzrlib.errors import StrictCommitFailed
 
282
        wt = self.make_branch_and_tree('.')
 
283
        b = wt.branch
 
284
        file('hello', 'w').write('hello world')
 
285
        wt.add('hello')
 
286
        wt.commit(message='add hello', strict=True)
 
287
 
272
288
    def test_nonstrict_commit(self):
273
289
        """Try and commit with unknown files and strict = False, should work."""
274
 
        b = Branch.initialize('.')
 
290
        wt = self.make_branch_and_tree('.')
 
291
        b = wt.branch
275
292
        file('hello', 'w').write('hello world')
276
 
        b.add('hello')
 
293
        wt.add('hello')
277
294
        file('goodbye', 'w').write('goodbye cruel world!')
278
 
        b.commit(message='add hello but not goodbye', strict=False)
 
295
        wt.commit(message='add hello but not goodbye', strict=False)
 
296
 
 
297
    def test_nonstrict_commit_without_unknowns(self):
 
298
        """Try and commit with no unknown files and strict = False,
 
299
        should work."""
 
300
        wt = self.make_branch_and_tree('.')
 
301
        b = wt.branch
 
302
        file('hello', 'w').write('hello world')
 
303
        wt.add('hello')
 
304
        wt.commit(message='add hello', strict=False)
279
305
 
280
306
    def test_signed_commit(self):
281
307
        import bzrlib.gpg
282
308
        import bzrlib.commit as commit
283
309
        oldstrategy = bzrlib.gpg.GPGStrategy
284
 
        branch = Branch.initialize('.')
285
 
        branch.commit("base", allow_pointless=True, rev_id='A')
286
 
        self.failIf(branch.revision_store.has_id('A', 'sig'))
 
310
        wt = self.make_branch_and_tree('.')
 
311
        branch = wt.branch
 
312
        wt.commit("base", allow_pointless=True, rev_id='A')
 
313
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
287
314
        try:
288
315
            from bzrlib.testament import Testament
289
316
            # monkey patch gpg signing mechanism
290
317
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
291
 
            commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
 
318
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
292
319
                                                      allow_pointless=True,
293
 
                                                      rev_id='B')
294
 
            self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
295
 
                             branch.revision_store.get('B', 'sig').read())
 
320
                                                      rev_id='B',
 
321
                                                      working_tree=wt)
 
322
            self.assertEqual(Testament.from_revision(branch.repository,
 
323
                             'B').as_short_text(),
 
324
                             branch.repository.get_signature_text('B'))
296
325
        finally:
297
326
            bzrlib.gpg.GPGStrategy = oldstrategy
298
327
 
300
329
        import bzrlib.gpg
301
330
        import bzrlib.commit as commit
302
331
        oldstrategy = bzrlib.gpg.GPGStrategy
303
 
        branch = Branch.initialize('.')
304
 
        branch.commit("base", allow_pointless=True, rev_id='A')
305
 
        self.failIf(branch.revision_store.has_id('A', 'sig'))
 
332
        wt = self.make_branch_and_tree('.')
 
333
        branch = wt.branch
 
334
        wt.commit("base", allow_pointless=True, rev_id='A')
 
335
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
306
336
        try:
307
337
            from bzrlib.testament import Testament
308
338
            # monkey patch gpg signing mechanism
310
340
            config = MustSignConfig(branch)
311
341
            self.assertRaises(SigningFailed,
312
342
                              commit.Commit(config=config).commit,
313
 
                              branch, "base",
 
343
                              message="base",
314
344
                              allow_pointless=True,
315
 
                              rev_id='B')
316
 
            branch = Branch.open('.')
 
345
                              rev_id='B',
 
346
                              working_tree=wt)
 
347
            branch = Branch.open(self.get_url('.'))
317
348
            self.assertEqual(branch.revision_history(), ['A'])
318
 
            self.failIf(branch.revision_store.has_id('B'))
 
349
            self.failIf(branch.repository.has_revision('B'))
319
350
        finally:
320
351
            bzrlib.gpg.GPGStrategy = oldstrategy
 
352
 
 
353
    def test_commit_invokes_hooks(self):
 
354
        import bzrlib.commit as commit
 
355
        wt = self.make_branch_and_tree('.')
 
356
        branch = wt.branch
 
357
        calls = []
 
358
        def called(branch, rev_id):
 
359
            calls.append('called')
 
360
        bzrlib.ahook = called
 
361
        try:
 
362
            config = BranchWithHooks(branch)
 
363
            commit.Commit(config=config).commit(
 
364
                            message = "base",
 
365
                            allow_pointless=True,
 
366
                            rev_id='A', working_tree = wt)
 
367
            self.assertEqual(['called', 'called'], calls)
 
368
        finally:
 
369
            del bzrlib.ahook
 
370
 
 
371
    def test_commit_object_doesnt_set_nick(self):
 
372
        # using the Commit object directly does not set the branch nick.
 
373
        wt = self.make_branch_and_tree('.')
 
374
        c = Commit()
 
375
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
 
376
        self.assertEquals(wt.branch.revno(), 1)
 
377
        self.assertEqual({},
 
378
                         wt.branch.repository.get_revision(
 
379
                            wt.branch.last_revision()).properties)
 
380
 
 
381
    def test_safe_master_lock(self):
 
382
        os.mkdir('master')
 
383
        master = BzrDirMetaFormat1().initialize('master')
 
384
        master.create_repository()
 
385
        master_branch = master.create_branch()
 
386
        master.create_workingtree()
 
387
        bound = master.sprout('bound')
 
388
        wt = bound.open_workingtree()
 
389
        wt.branch.set_bound_location(os.path.realpath('master'))
 
390
        master_branch.lock_write()
 
391
        try:
 
392
            self.assertRaises(LockContention, wt.commit, 'silly')
 
393
        finally:
 
394
            master_branch.unlock()