~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_commit.py

  • Committer: Robert Collins
  • Date: 2005-10-18 13:11:57 UTC
  • mfrom: (1185.16.72) (0.2.1)
  • Revision ID: robertc@robertcollins.net-20051018131157-76a9970aa78e927e
Merged Martin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
 
18
18
import os
19
19
 
20
 
import bzrlib
21
 
from bzrlib import (
22
 
    errors,
23
 
    lockdir,
24
 
    )
 
20
from bzrlib.selftest import TestCaseInTempDir
25
21
from bzrlib.branch import Branch
26
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
27
 
from bzrlib.commit import Commit, NullCommitReporter
 
22
from bzrlib.workingtree import WorkingTree
 
23
from bzrlib.commit import Commit
28
24
from bzrlib.config import BranchConfig
29
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
30
 
                           LockContention)
31
 
from bzrlib.tests import TestCaseWithTransport
32
 
from bzrlib.workingtree import WorkingTree
 
25
from bzrlib.errors import PointlessCommit, BzrError, SigningFailed
33
26
 
34
27
 
35
28
# TODO: Test commit with some added, and added-but-missing files
43
36
        return ['cat', '-']
44
37
 
45
38
 
46
 
class BranchWithHooks(BranchConfig):
47
 
 
48
 
    def post_commit(self):
49
 
        return "bzrlib.ahook bzrlib.ahook"
50
 
 
51
 
 
52
 
class CapturingReporter(NullCommitReporter):
53
 
    """This reporter captures the calls made to it for evaluation later."""
54
 
 
55
 
    def __init__(self):
56
 
        # a list of the calls this received
57
 
        self.calls = []
58
 
 
59
 
    def snapshot_change(self, change, path):
60
 
        self.calls.append(('change', change, path))
61
 
 
62
 
    def deleted(self, file_id):
63
 
        self.calls.append(('deleted', file_id))
64
 
 
65
 
    def missing(self, path):
66
 
        self.calls.append(('missing', path))
67
 
 
68
 
    def renamed(self, change, old_path, new_path):
69
 
        self.calls.append(('renamed', change, old_path, new_path))
70
 
 
71
 
 
72
 
class TestCommit(TestCaseWithTransport):
 
39
class TestCommit(TestCaseInTempDir):
73
40
 
74
41
    def test_simple_commit(self):
75
42
        """Commit and check two versions of a single file."""
76
 
        wt = self.make_branch_and_tree('.')
77
 
        b = wt.branch
 
43
        b = Branch.initialize('.')
78
44
        file('hello', 'w').write('hello world')
79
 
        wt.add('hello')
80
 
        wt.commit(message='add hello')
81
 
        file_id = wt.path2id('hello')
 
45
        b.add('hello')
 
46
        b.commit(message='add hello')
 
47
        file_id = b.working_tree().path2id('hello')
82
48
 
83
49
        file('hello', 'w').write('version 2')
84
 
        wt.commit(message='commit 2')
 
50
        b.commit(message='commit 2')
85
51
 
86
52
        eq = self.assertEquals
87
53
        eq(b.revno(), 2)
88
54
        rh = b.revision_history()
89
 
        rev = b.repository.get_revision(rh[0])
 
55
        rev = b.get_revision(rh[0])
90
56
        eq(rev.message, 'add hello')
91
57
 
92
 
        tree1 = b.repository.revision_tree(rh[0])
 
58
        tree1 = b.revision_tree(rh[0])
93
59
        text = tree1.get_file_text(file_id)
94
60
        eq(text, 'hello world')
95
61
 
96
 
        tree2 = b.repository.revision_tree(rh[1])
 
62
        tree2 = b.revision_tree(rh[1])
97
63
        eq(tree2.get_file_text(file_id), 'version 2')
98
64
 
99
65
    def test_delete_commit(self):
100
66
        """Test a commit with a deleted file"""
101
 
        wt = self.make_branch_and_tree('.')
102
 
        b = wt.branch
 
67
        b = Branch.initialize('.')
103
68
        file('hello', 'w').write('hello world')
104
 
        wt.add(['hello'], ['hello-id'])
105
 
        wt.commit(message='add hello')
 
69
        b.add(['hello'], ['hello-id'])
 
70
        b.commit(message='add hello')
106
71
 
107
72
        os.remove('hello')
108
 
        wt.commit('removed hello', rev_id='rev2')
 
73
        b.commit('removed hello', rev_id='rev2')
109
74
 
110
 
        tree = b.repository.revision_tree('rev2')
 
75
        tree = b.revision_tree('rev2')
111
76
        self.assertFalse(tree.has_id('hello-id'))
112
77
 
 
78
 
113
79
    def test_pointless_commit(self):
114
80
        """Commit refuses unless there are changes or it's forced."""
115
 
        wt = self.make_branch_and_tree('.')
116
 
        b = wt.branch
 
81
        b = Branch.initialize('.')
117
82
        file('hello', 'w').write('hello')
118
 
        wt.add(['hello'])
119
 
        wt.commit(message='add hello')
 
83
        b.add(['hello'])
 
84
        b.commit(message='add hello')
120
85
        self.assertEquals(b.revno(), 1)
121
86
        self.assertRaises(PointlessCommit,
122
 
                          wt.commit,
 
87
                          b.commit,
123
88
                          message='fails',
124
89
                          allow_pointless=False)
125
90
        self.assertEquals(b.revno(), 1)
126
91
        
 
92
 
 
93
 
127
94
    def test_commit_empty(self):
128
95
        """Commiting an empty tree works."""
129
 
        wt = self.make_branch_and_tree('.')
130
 
        b = wt.branch
131
 
        wt.commit(message='empty tree', allow_pointless=True)
 
96
        b = Branch.initialize('.')
 
97
        b.commit(message='empty tree', allow_pointless=True)
132
98
        self.assertRaises(PointlessCommit,
133
 
                          wt.commit,
 
99
                          b.commit,
134
100
                          message='empty tree',
135
101
                          allow_pointless=False)
136
 
        wt.commit(message='empty tree', allow_pointless=True)
 
102
        b.commit(message='empty tree', allow_pointless=True)
137
103
        self.assertEquals(b.revno(), 2)
138
104
 
 
105
 
139
106
    def test_selective_delete(self):
140
107
        """Selective commit in tree with deletions"""
141
 
        wt = self.make_branch_and_tree('.')
142
 
        b = wt.branch
 
108
        b = Branch.initialize('.')
143
109
        file('hello', 'w').write('hello')
144
110
        file('buongia', 'w').write('buongia')
145
 
        wt.add(['hello', 'buongia'],
 
111
        b.add(['hello', 'buongia'],
146
112
              ['hello-id', 'buongia-id'])
147
 
        wt.commit(message='add files',
 
113
        b.commit(message='add files',
148
114
                 rev_id='test@rev-1')
149
115
        
150
116
        os.remove('hello')
151
117
        file('buongia', 'w').write('new text')
152
 
        wt.commit(message='update text',
 
118
        b.commit(message='update text',
153
119
                 specific_files=['buongia'],
154
120
                 allow_pointless=False,
155
121
                 rev_id='test@rev-2')
156
122
 
157
 
        wt.commit(message='remove hello',
 
123
        b.commit(message='remove hello',
158
124
                 specific_files=['hello'],
159
125
                 allow_pointless=False,
160
126
                 rev_id='test@rev-3')
162
128
        eq = self.assertEquals
163
129
        eq(b.revno(), 3)
164
130
 
165
 
        tree2 = b.repository.revision_tree('test@rev-2')
 
131
        tree2 = b.revision_tree('test@rev-2')
166
132
        self.assertTrue(tree2.has_filename('hello'))
167
133
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
168
134
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
169
135
        
170
 
        tree3 = b.repository.revision_tree('test@rev-3')
 
136
        tree3 = b.revision_tree('test@rev-3')
171
137
        self.assertFalse(tree3.has_filename('hello'))
172
138
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
173
139
 
 
140
 
174
141
    def test_commit_rename(self):
175
142
        """Test commit of a revision where a file is renamed."""
176
 
        tree = self.make_branch_and_tree('.')
177
 
        b = tree.branch
178
 
        self.build_tree(['hello'], line_endings='binary')
179
 
        tree.add(['hello'], ['hello-id'])
180
 
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
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)
181
147
 
182
 
        tree.rename_one('hello', 'fruity')
183
 
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
148
        b.rename_one('hello', 'fruity')
 
149
        b.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
184
150
 
185
151
        eq = self.assertEquals
186
 
        tree1 = b.repository.revision_tree('test@rev-1')
 
152
        tree1 = b.revision_tree('test@rev-1')
187
153
        eq(tree1.id2path('hello-id'), 'hello')
188
154
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
189
155
        self.assertFalse(tree1.has_filename('fruity'))
191
157
        ie = tree1.inventory['hello-id']
192
158
        eq(ie.revision, 'test@rev-1')
193
159
 
194
 
        tree2 = b.repository.revision_tree('test@rev-2')
 
160
        tree2 = b.revision_tree('test@rev-2')
195
161
        eq(tree2.id2path('hello-id'), 'fruity')
196
162
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
197
163
        self.check_inventory_shape(tree2.inventory, ['fruity'])
198
164
        ie = tree2.inventory['hello-id']
199
165
        eq(ie.revision, 'test@rev-2')
200
166
 
 
167
 
201
168
    def test_reused_rev_id(self):
202
169
        """Test that a revision id cannot be reused in a branch"""
203
 
        wt = self.make_branch_and_tree('.')
204
 
        b = wt.branch
205
 
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
 
170
        b = Branch.initialize('.')
 
171
        b.commit('initial', rev_id='test@rev-1', allow_pointless=True)
206
172
        self.assertRaises(Exception,
207
 
                          wt.commit,
 
173
                          b.commit,
208
174
                          message='reused id',
209
175
                          rev_id='test@rev-1',
210
176
                          allow_pointless=True)
 
177
                          
 
178
 
211
179
 
212
180
    def test_commit_move(self):
213
181
        """Test commit of revisions with moved files and directories"""
214
182
        eq = self.assertEquals
215
 
        wt = self.make_branch_and_tree('.')
216
 
        b = wt.branch
 
183
        b = Branch.initialize('.')
217
184
        r1 = 'test@rev-1'
218
185
        self.build_tree(['hello', 'a/', 'b/'])
219
 
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
220
 
        wt.commit('initial', rev_id=r1, allow_pointless=False)
221
 
        wt.move(['hello'], 'a')
 
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')
222
190
        r2 = 'test@rev-2'
223
 
        wt.commit('two', rev_id=r2, allow_pointless=False)
224
 
        self.check_inventory_shape(wt.read_working_inventory(),
 
191
        b.commit('two', rev_id=r2, allow_pointless=False)
 
192
        self.check_inventory_shape(b.inventory,
225
193
                                   ['a', 'a/hello', 'b'])
226
194
 
227
 
        wt.move(['b'], 'a')
 
195
        b.move(['b'], 'a')
228
196
        r3 = 'test@rev-3'
229
 
        wt.commit('three', rev_id=r3, allow_pointless=False)
230
 
        self.check_inventory_shape(wt.read_working_inventory(),
 
197
        b.commit('three', rev_id=r3, allow_pointless=False)
 
198
        self.check_inventory_shape(b.inventory,
231
199
                                   ['a', 'a/hello', 'a/b'])
232
 
        self.check_inventory_shape(b.repository.get_revision_inventory(r3),
 
200
        self.check_inventory_shape(b.get_revision_inventory(r3),
233
201
                                   ['a', 'a/hello', 'a/b'])
234
202
 
235
 
        wt.move(['a/hello'], 'a/b')
 
203
        b.move([os.sep.join(['a', 'hello'])],
 
204
               os.sep.join(['a', 'b']))
236
205
        r4 = 'test@rev-4'
237
 
        wt.commit('four', rev_id=r4, allow_pointless=False)
238
 
        self.check_inventory_shape(wt.read_working_inventory(),
 
206
        b.commit('four', rev_id=r4, allow_pointless=False)
 
207
        self.check_inventory_shape(b.inventory,
239
208
                                   ['a', 'a/b/hello', 'a/b'])
240
209
 
241
 
        inv = b.repository.get_revision_inventory(r4)
 
210
        inv = b.get_revision_inventory(r4)
242
211
        eq(inv['hello-id'].revision, r4)
243
212
        eq(inv['a-id'].revision, r1)
244
213
        eq(inv['b-id'].revision, r3)
 
214
 
245
215
        
246
216
    def test_removed_commit(self):
247
217
        """Commit with a removed file"""
248
 
        wt = self.make_branch_and_tree('.')
249
 
        b = wt.branch
 
218
        b = Branch.initialize('.')
 
219
        wt = b.working_tree()
250
220
        file('hello', 'w').write('hello world')
251
 
        wt.add(['hello'], ['hello-id'])
252
 
        wt.commit(message='add hello')
 
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
253
225
        wt.remove('hello')
254
 
        wt.commit('removed hello', rev_id='rev2')
 
226
        b.commit('removed hello', rev_id='rev2')
255
227
 
256
 
        tree = b.repository.revision_tree('rev2')
 
228
        tree = b.revision_tree('rev2')
257
229
        self.assertFalse(tree.has_id('hello-id'))
258
230
 
 
231
 
259
232
    def test_committed_ancestry(self):
260
233
        """Test commit appends revisions to ancestry."""
261
 
        wt = self.make_branch_and_tree('.')
262
 
        b = wt.branch
 
234
        b = Branch.initialize('.')
263
235
        rev_ids = []
264
236
        for i in range(4):
265
237
            file('hello', 'w').write((str(i) * 4) + '\n')
266
238
            if i == 0:
267
 
                wt.add(['hello'], ['hello-id'])
 
239
                b.add(['hello'], ['hello-id'])
268
240
            rev_id = 'test@rev-%d' % (i+1)
269
241
            rev_ids.append(rev_id)
270
 
            wt.commit(message='rev %d' % (i+1),
 
242
            b.commit(message='rev %d' % (i+1),
271
243
                     rev_id=rev_id)
272
244
        eq = self.assertEquals
273
245
        eq(b.revision_history(), rev_ids)
274
246
        for i in range(4):
275
 
            anc = b.repository.get_ancestry(rev_ids[i])
 
247
            anc = b.get_ancestry(rev_ids[i])
276
248
            eq(anc, [None] + rev_ids[:i+1])
277
249
 
278
250
    def test_commit_new_subdir_child_selective(self):
279
 
        wt = self.make_branch_and_tree('.')
280
 
        b = wt.branch
 
251
        b = Branch.initialize('.')
281
252
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
282
 
        wt.add(['dir', 'dir/file1', 'dir/file2'],
 
253
        b.add(['dir', 'dir/file1', 'dir/file2'],
283
254
              ['dirid', 'file1id', 'file2id'])
284
 
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
285
 
        inv = b.repository.get_inventory('1')
 
255
        b.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
 
256
        inv = b.get_inventory('1')
286
257
        self.assertEqual('1', inv['dirid'].revision)
287
258
        self.assertEqual('1', inv['file1id'].revision)
288
259
        # FIXME: This should raise a KeyError I think, rbc20051006
291
262
    def test_strict_commit(self):
292
263
        """Try and commit with unknown files and strict = True, should fail."""
293
264
        from bzrlib.errors import StrictCommitFailed
294
 
        wt = self.make_branch_and_tree('.')
295
 
        b = wt.branch
 
265
        b = Branch.initialize('.')
296
266
        file('hello', 'w').write('hello world')
297
 
        wt.add('hello')
 
267
        b.add('hello')
298
268
        file('goodbye', 'w').write('goodbye cruel world!')
299
 
        self.assertRaises(StrictCommitFailed, wt.commit,
 
269
        self.assertRaises(StrictCommitFailed, b.commit,
300
270
            message='add hello but not goodbye', strict=True)
301
271
 
302
 
    def test_strict_commit_without_unknowns(self):
303
 
        """Try and commit with no unknown files and strict = True,
304
 
        should work."""
305
 
        from bzrlib.errors import StrictCommitFailed
306
 
        wt = self.make_branch_and_tree('.')
307
 
        b = wt.branch
308
 
        file('hello', 'w').write('hello world')
309
 
        wt.add('hello')
310
 
        wt.commit(message='add hello', strict=True)
311
 
 
312
272
    def test_nonstrict_commit(self):
313
273
        """Try and commit with unknown files and strict = False, should work."""
314
 
        wt = self.make_branch_and_tree('.')
315
 
        b = wt.branch
 
274
        b = Branch.initialize('.')
316
275
        file('hello', 'w').write('hello world')
317
 
        wt.add('hello')
 
276
        b.add('hello')
318
277
        file('goodbye', 'w').write('goodbye cruel world!')
319
 
        wt.commit(message='add hello but not goodbye', strict=False)
320
 
 
321
 
    def test_nonstrict_commit_without_unknowns(self):
322
 
        """Try and commit with no unknown files and strict = False,
323
 
        should work."""
324
 
        wt = self.make_branch_and_tree('.')
325
 
        b = wt.branch
326
 
        file('hello', 'w').write('hello world')
327
 
        wt.add('hello')
328
 
        wt.commit(message='add hello', strict=False)
 
278
        b.commit(message='add hello but not goodbye', strict=False)
329
279
 
330
280
    def test_signed_commit(self):
331
281
        import bzrlib.gpg
332
282
        import bzrlib.commit as commit
333
283
        oldstrategy = bzrlib.gpg.GPGStrategy
334
 
        wt = self.make_branch_and_tree('.')
335
 
        branch = wt.branch
336
 
        wt.commit("base", allow_pointless=True, rev_id='A')
337
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
284
        branch = Branch.initialize('.')
 
285
        branch.commit("base", allow_pointless=True, rev_id='A')
 
286
        self.failIf(branch.revision_store.has_id('A', 'sig'))
338
287
        try:
339
288
            from bzrlib.testament import Testament
340
289
            # monkey patch gpg signing mechanism
341
290
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
342
 
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
 
291
            commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
343
292
                                                      allow_pointless=True,
344
 
                                                      rev_id='B',
345
 
                                                      working_tree=wt)
346
 
            self.assertEqual(Testament.from_revision(branch.repository,
347
 
                             'B').as_short_text(),
348
 
                             branch.repository.get_signature_text('B'))
 
293
                                                      rev_id='B')
 
294
            self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
 
295
                             branch.revision_store.get('B', 'sig').read())
349
296
        finally:
350
297
            bzrlib.gpg.GPGStrategy = oldstrategy
351
298
 
353
300
        import bzrlib.gpg
354
301
        import bzrlib.commit as commit
355
302
        oldstrategy = bzrlib.gpg.GPGStrategy
356
 
        wt = self.make_branch_and_tree('.')
357
 
        branch = wt.branch
358
 
        wt.commit("base", allow_pointless=True, rev_id='A')
359
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
303
        branch = Branch.initialize('.')
 
304
        branch.commit("base", allow_pointless=True, rev_id='A')
 
305
        self.failIf(branch.revision_store.has_id('A', 'sig'))
360
306
        try:
361
307
            from bzrlib.testament import Testament
362
308
            # monkey patch gpg signing mechanism
364
310
            config = MustSignConfig(branch)
365
311
            self.assertRaises(SigningFailed,
366
312
                              commit.Commit(config=config).commit,
367
 
                              message="base",
 
313
                              branch, "base",
368
314
                              allow_pointless=True,
369
 
                              rev_id='B',
370
 
                              working_tree=wt)
371
 
            branch = Branch.open(self.get_url('.'))
 
315
                              rev_id='B')
 
316
            branch = Branch.open('.')
372
317
            self.assertEqual(branch.revision_history(), ['A'])
373
 
            self.failIf(branch.repository.has_revision('B'))
 
318
            self.failIf(branch.revision_store.has_id('B'))
374
319
        finally:
375
320
            bzrlib.gpg.GPGStrategy = oldstrategy
376
 
 
377
 
    def test_commit_invokes_hooks(self):
378
 
        import bzrlib.commit as commit
379
 
        wt = self.make_branch_and_tree('.')
380
 
        branch = wt.branch
381
 
        calls = []
382
 
        def called(branch, rev_id):
383
 
            calls.append('called')
384
 
        bzrlib.ahook = called
385
 
        try:
386
 
            config = BranchWithHooks(branch)
387
 
            commit.Commit(config=config).commit(
388
 
                            message = "base",
389
 
                            allow_pointless=True,
390
 
                            rev_id='A', working_tree = wt)
391
 
            self.assertEqual(['called', 'called'], calls)
392
 
        finally:
393
 
            del bzrlib.ahook
394
 
 
395
 
    def test_commit_object_doesnt_set_nick(self):
396
 
        # using the Commit object directly does not set the branch nick.
397
 
        wt = self.make_branch_and_tree('.')
398
 
        c = Commit()
399
 
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
400
 
        self.assertEquals(wt.branch.revno(), 1)
401
 
        self.assertEqual({},
402
 
                         wt.branch.repository.get_revision(
403
 
                            wt.branch.last_revision()).properties)
404
 
 
405
 
    def test_safe_master_lock(self):
406
 
        os.mkdir('master')
407
 
        master = BzrDirMetaFormat1().initialize('master')
408
 
        master.create_repository()
409
 
        master_branch = master.create_branch()
410
 
        master.create_workingtree()
411
 
        bound = master.sprout('bound')
412
 
        wt = bound.open_workingtree()
413
 
        wt.branch.set_bound_location(os.path.realpath('master'))
414
 
 
415
 
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
416
 
        master_branch.lock_write()
417
 
        try:
418
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
419
 
            self.assertRaises(LockContention, wt.commit, 'silly')
420
 
        finally:
421
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
422
 
            master_branch.unlock()
423
 
 
424
 
    def test_commit_bound_merge(self):
425
 
        # see bug #43959; commit of a merge in a bound branch fails to push
426
 
        # the new commit into the master
427
 
        master_branch = self.make_branch('master')
428
 
        bound_tree = self.make_branch_and_tree('bound')
429
 
        bound_tree.branch.bind(master_branch)
430
 
 
431
 
        self.build_tree_contents([('bound/content_file', 'initial contents\n')])
432
 
        bound_tree.add(['content_file'])
433
 
        bound_tree.commit(message='woo!')
434
 
 
435
 
        other_bzrdir = master_branch.bzrdir.sprout('other')
436
 
        other_tree = other_bzrdir.open_workingtree()
437
 
 
438
 
        # do a commit to the the other branch changing the content file so
439
 
        # that our commit after merging will have a merged revision in the
440
 
        # content file history.
441
 
        self.build_tree_contents([('other/content_file', 'change in other\n')])
442
 
        other_tree.commit('change in other')
443
 
 
444
 
        # do a merge into the bound branch from other, and then change the
445
 
        # content file locally to force a new revision (rather than using the
446
 
        # revision from other). This forces extra processing in commit.
447
 
        bound_tree.merge_from_branch(other_tree.branch)
448
 
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
449
 
 
450
 
        # before #34959 was fixed, this failed with 'revision not present in
451
 
        # weave' when trying to implicitly push from the bound branch to the master
452
 
        bound_tree.commit(message='commit of merge in bound tree')
453
 
 
454
 
    def test_commit_reporting_after_merge(self):
455
 
        # when doing a commit of a merge, the reporter needs to still 
456
 
        # be called for each item that is added/removed/deleted.
457
 
        this_tree = self.make_branch_and_tree('this')
458
 
        # we need a bunch of files and dirs, to perform one action on each.
459
 
        self.build_tree([
460
 
            'this/dirtorename/',
461
 
            'this/dirtoreparent/',
462
 
            'this/dirtoleave/',
463
 
            'this/dirtoremove/',
464
 
            'this/filetoreparent',
465
 
            'this/filetorename',
466
 
            'this/filetomodify',
467
 
            'this/filetoremove',
468
 
            'this/filetoleave']
469
 
            )
470
 
        this_tree.add([
471
 
            'dirtorename',
472
 
            'dirtoreparent',
473
 
            'dirtoleave',
474
 
            'dirtoremove',
475
 
            'filetoreparent',
476
 
            'filetorename',
477
 
            'filetomodify',
478
 
            'filetoremove',
479
 
            'filetoleave']
480
 
            )
481
 
        this_tree.commit('create_files')
482
 
        other_dir = this_tree.bzrdir.sprout('other')
483
 
        other_tree = other_dir.open_workingtree()
484
 
        other_tree.lock_write()
485
 
        # perform the needed actions on the files and dirs.
486
 
        try:
487
 
            other_tree.rename_one('dirtorename', 'renameddir')
488
 
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
489
 
            other_tree.rename_one('filetorename', 'renamedfile')
490
 
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
491
 
            other_tree.remove(['dirtoremove', 'filetoremove'])
492
 
            self.build_tree_contents([
493
 
                ('other/newdir/', ),
494
 
                ('other/filetomodify', 'new content'),
495
 
                ('other/newfile', 'new file content')])
496
 
            other_tree.add('newfile')
497
 
            other_tree.add('newdir/')
498
 
            other_tree.commit('modify all sample files and dirs.')
499
 
        finally:
500
 
            other_tree.unlock()
501
 
        this_tree.merge_from_branch(other_tree.branch)
502
 
        reporter = CapturingReporter()
503
 
        this_tree.commit('do the commit', reporter=reporter)
504
 
        self.assertEqual([
505
 
            ('change', 'unchanged', ''),
506
 
            ('change', 'unchanged', 'dirtoleave'),
507
 
            ('change', 'unchanged', 'filetoleave'),
508
 
            ('change', 'modified', 'filetomodify'),
509
 
            ('change', 'added', 'newdir'),
510
 
            ('change', 'added', 'newfile'),
511
 
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
512
 
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
513
 
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
514
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
515
 
            ('deleted', 'dirtoremove'),
516
 
            ('deleted', 'filetoremove'),
517
 
            ],
518
 
            reporter.calls)
519
 
 
520
 
    def test_commit_removals_respects_filespec(self):
521
 
        """Commit respects the specified_files for removals."""
522
 
        tree = self.make_branch_and_tree('.')
523
 
        self.build_tree(['a', 'b'])
524
 
        tree.add(['a', 'b'])
525
 
        tree.commit('added a, b')
526
 
        tree.remove(['a', 'b'])
527
 
        tree.commit('removed a', specific_files='a')
528
 
        basis = tree.basis_tree().inventory
529
 
        self.assertIs(None, basis.path2id('a'))
530
 
        self.assertFalse(basis.path2id('b') is None)
531
 
 
532
 
    def test_commit_saves_1ms_timestamp(self):
533
 
        """Passing in a timestamp is saved with 1ms resolution"""
534
 
        tree = self.make_branch_and_tree('.')
535
 
        self.build_tree(['a'])
536
 
        tree.add('a')
537
 
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
538
 
                    rev_id='a1')
539
 
 
540
 
        rev = tree.branch.repository.get_revision('a1')
541
 
        self.assertEqual(1153248633.419, rev.timestamp)
542
 
 
543
 
    def test_commit_has_1ms_resolution(self):
544
 
        """Allowing commit to generate the timestamp also has 1ms resolution"""
545
 
        tree = self.make_branch_and_tree('.')
546
 
        self.build_tree(['a'])
547
 
        tree.add('a')
548
 
        tree.commit('added a', rev_id='a1')
549
 
 
550
 
        rev = tree.branch.repository.get_revision('a1')
551
 
        timestamp = rev.timestamp
552
 
        timestamp_1ms = round(timestamp, 3)
553
 
        self.assertEqual(timestamp_1ms, timestamp)
554
 
 
555
 
    def test_commit_unversioned_specified(self):
556
 
        """Commit should raise if specified files isn't in basis or worktree"""
557
 
        tree = self.make_branch_and_tree('.')
558
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
559
 
                          'message', specific_files=['bogus'])
560
 
 
561
 
    class Callback(object):
562
 
        
563
 
        def __init__(self, message, testcase):
564
 
            self.called = False
565
 
            self.message = message
566
 
            self.testcase = testcase
567
 
 
568
 
        def __call__(self, commit_obj):
569
 
            self.called = True
570
 
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
571
 
            return self.message
572
 
 
573
 
    def test_commit_callback(self):
574
 
        """Commit should invoke a callback to get the message"""
575
 
 
576
 
        tree = self.make_branch_and_tree('.')
577
 
        try:
578
 
            tree.commit()
579
 
        except Exception, e:
580
 
            self.assertTrue(isinstance(e, BzrError))
581
 
            self.assertEqual('The message or message_callback keyword'
582
 
                             ' parameter is required for commit().', str(e))
583
 
        else:
584
 
            self.fail('exception not raised')
585
 
        cb = self.Callback(u'commit 1', self)
586
 
        tree.commit(message_callback=cb)
587
 
        self.assertTrue(cb.called)
588
 
        repository = tree.branch.repository
589
 
        message = repository.get_revision(tree.last_revision()).message
590
 
        self.assertEqual('commit 1', message)
591
 
 
592
 
    def test_no_callback_pointless(self):
593
 
        """Callback should not be invoked for pointless commit"""
594
 
        tree = self.make_branch_and_tree('.')
595
 
        cb = self.Callback(u'commit 2', self)
596
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
597
 
                          allow_pointless=False)
598
 
        self.assertFalse(cb.called)
599
 
 
600
 
    def test_no_callback_netfailure(self):
601
 
        """Callback should not be invoked if connectivity fails"""
602
 
        tree = self.make_branch_and_tree('.')
603
 
        cb = self.Callback(u'commit 2', self)
604
 
        repository = tree.branch.repository
605
 
        # simulate network failure
606
 
        def raise_(self, arg, arg2):
607
 
            raise errors.NoSuchFile('foo')
608
 
        repository.add_inventory = raise_
609
 
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
610
 
        self.assertFalse(cb.called)