~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_commit.py

  • Committer: Martin Pool
  • Date: 2005-11-04 01:46:31 UTC
  • mto: (1185.33.49 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 1512.
  • Revision ID: mbp@sourcefrog.net-20051104014631-750e0ad4172c952c
Make biobench directly executable

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
18
18
import os
19
19
 
20
20
import bzrlib
21
 
from bzrlib import (
22
 
    bzrdir,
23
 
    errors,
24
 
    lockdir,
25
 
    osutils,
26
 
    tests,
27
 
    )
 
21
from bzrlib.selftest import TestCaseInTempDir
28
22
from bzrlib.branch import Branch
29
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
30
 
from bzrlib.commit import Commit, NullCommitReporter
 
23
from bzrlib.workingtree import WorkingTree
 
24
from bzrlib.commit import Commit
31
25
from bzrlib.config import BranchConfig
32
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed, 
33
 
                           LockContention)
34
 
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
35
 
from bzrlib.workingtree import WorkingTree
 
26
from bzrlib.errors import PointlessCommit, BzrError, SigningFailed
36
27
 
37
28
 
38
29
# TODO: Test commit with some added, and added-but-missing files
52
43
        return "bzrlib.ahook bzrlib.ahook"
53
44
 
54
45
 
55
 
class CapturingReporter(NullCommitReporter):
56
 
    """This reporter captures the calls made to it for evaluation later."""
57
 
 
58
 
    def __init__(self):
59
 
        # a list of the calls this received
60
 
        self.calls = []
61
 
 
62
 
    def snapshot_change(self, change, path):
63
 
        self.calls.append(('change', change, path))
64
 
 
65
 
    def deleted(self, file_id):
66
 
        self.calls.append(('deleted', file_id))
67
 
 
68
 
    def missing(self, path):
69
 
        self.calls.append(('missing', path))
70
 
 
71
 
    def renamed(self, change, old_path, new_path):
72
 
        self.calls.append(('renamed', change, old_path, new_path))
73
 
 
74
 
    def is_verbose(self):
75
 
        return True
76
 
 
77
 
 
78
 
class TestCommit(TestCaseWithTransport):
 
46
class TestCommit(TestCaseInTempDir):
79
47
 
80
48
    def test_simple_commit(self):
81
49
        """Commit and check two versions of a single file."""
82
 
        wt = self.make_branch_and_tree('.')
83
 
        b = wt.branch
 
50
        b = Branch.initialize('.')
84
51
        file('hello', 'w').write('hello world')
85
 
        wt.add('hello')
86
 
        wt.commit(message='add hello')
87
 
        file_id = wt.path2id('hello')
 
52
        b.add('hello')
 
53
        b.commit(message='add hello')
 
54
        file_id = b.working_tree().path2id('hello')
88
55
 
89
56
        file('hello', 'w').write('version 2')
90
 
        wt.commit(message='commit 2')
 
57
        b.commit(message='commit 2')
91
58
 
92
59
        eq = self.assertEquals
93
60
        eq(b.revno(), 2)
94
61
        rh = b.revision_history()
95
 
        rev = b.repository.get_revision(rh[0])
 
62
        rev = b.get_revision(rh[0])
96
63
        eq(rev.message, 'add hello')
97
64
 
98
 
        tree1 = b.repository.revision_tree(rh[0])
99
 
        tree1.lock_read()
 
65
        tree1 = b.revision_tree(rh[0])
100
66
        text = tree1.get_file_text(file_id)
101
 
        tree1.unlock()
102
 
        self.assertEqual('hello world', text)
 
67
        eq(text, 'hello world')
103
68
 
104
 
        tree2 = b.repository.revision_tree(rh[1])
105
 
        tree2.lock_read()
106
 
        text = tree2.get_file_text(file_id)
107
 
        tree2.unlock()
108
 
        self.assertEqual('version 2', text)
 
69
        tree2 = b.revision_tree(rh[1])
 
70
        eq(tree2.get_file_text(file_id), 'version 2')
109
71
 
110
72
    def test_delete_commit(self):
111
73
        """Test a commit with a deleted file"""
112
 
        wt = self.make_branch_and_tree('.')
113
 
        b = wt.branch
 
74
        b = Branch.initialize('.')
114
75
        file('hello', 'w').write('hello world')
115
 
        wt.add(['hello'], ['hello-id'])
116
 
        wt.commit(message='add hello')
 
76
        b.add(['hello'], ['hello-id'])
 
77
        b.commit(message='add hello')
117
78
 
118
79
        os.remove('hello')
119
 
        wt.commit('removed hello', rev_id='rev2')
 
80
        b.commit('removed hello', rev_id='rev2')
120
81
 
121
 
        tree = b.repository.revision_tree('rev2')
 
82
        tree = b.revision_tree('rev2')
122
83
        self.assertFalse(tree.has_id('hello-id'))
123
84
 
 
85
 
124
86
    def test_pointless_commit(self):
125
87
        """Commit refuses unless there are changes or it's forced."""
126
 
        wt = self.make_branch_and_tree('.')
127
 
        b = wt.branch
 
88
        b = Branch.initialize('.')
128
89
        file('hello', 'w').write('hello')
129
 
        wt.add(['hello'])
130
 
        wt.commit(message='add hello')
 
90
        b.add(['hello'])
 
91
        b.commit(message='add hello')
131
92
        self.assertEquals(b.revno(), 1)
132
93
        self.assertRaises(PointlessCommit,
133
 
                          wt.commit,
 
94
                          b.commit,
134
95
                          message='fails',
135
96
                          allow_pointless=False)
136
97
        self.assertEquals(b.revno(), 1)
137
98
        
 
99
 
 
100
 
138
101
    def test_commit_empty(self):
139
102
        """Commiting an empty tree works."""
140
 
        wt = self.make_branch_and_tree('.')
141
 
        b = wt.branch
142
 
        wt.commit(message='empty tree', allow_pointless=True)
 
103
        b = Branch.initialize('.')
 
104
        b.commit(message='empty tree', allow_pointless=True)
143
105
        self.assertRaises(PointlessCommit,
144
 
                          wt.commit,
 
106
                          b.commit,
145
107
                          message='empty tree',
146
108
                          allow_pointless=False)
147
 
        wt.commit(message='empty tree', allow_pointless=True)
 
109
        b.commit(message='empty tree', allow_pointless=True)
148
110
        self.assertEquals(b.revno(), 2)
149
111
 
 
112
 
150
113
    def test_selective_delete(self):
151
114
        """Selective commit in tree with deletions"""
152
 
        wt = self.make_branch_and_tree('.')
153
 
        b = wt.branch
 
115
        b = Branch.initialize('.')
154
116
        file('hello', 'w').write('hello')
155
117
        file('buongia', 'w').write('buongia')
156
 
        wt.add(['hello', 'buongia'],
 
118
        b.add(['hello', 'buongia'],
157
119
              ['hello-id', 'buongia-id'])
158
 
        wt.commit(message='add files',
 
120
        b.commit(message='add files',
159
121
                 rev_id='test@rev-1')
160
122
        
161
123
        os.remove('hello')
162
124
        file('buongia', 'w').write('new text')
163
 
        wt.commit(message='update text',
 
125
        b.commit(message='update text',
164
126
                 specific_files=['buongia'],
165
127
                 allow_pointless=False,
166
128
                 rev_id='test@rev-2')
167
129
 
168
 
        wt.commit(message='remove hello',
 
130
        b.commit(message='remove hello',
169
131
                 specific_files=['hello'],
170
132
                 allow_pointless=False,
171
133
                 rev_id='test@rev-3')
173
135
        eq = self.assertEquals
174
136
        eq(b.revno(), 3)
175
137
 
176
 
        tree2 = b.repository.revision_tree('test@rev-2')
177
 
        tree2.lock_read()
178
 
        self.addCleanup(tree2.unlock)
 
138
        tree2 = b.revision_tree('test@rev-2')
179
139
        self.assertTrue(tree2.has_filename('hello'))
180
140
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
181
141
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
182
142
        
183
 
        tree3 = b.repository.revision_tree('test@rev-3')
184
 
        tree3.lock_read()
185
 
        self.addCleanup(tree3.unlock)
 
143
        tree3 = b.revision_tree('test@rev-3')
186
144
        self.assertFalse(tree3.has_filename('hello'))
187
145
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
188
146
 
 
147
 
189
148
    def test_commit_rename(self):
190
149
        """Test commit of a revision where a file is renamed."""
191
 
        tree = self.make_branch_and_tree('.')
192
 
        b = tree.branch
193
 
        self.build_tree(['hello'], line_endings='binary')
194
 
        tree.add(['hello'], ['hello-id'])
195
 
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
150
        b = Branch.initialize('.')
 
151
        self.build_tree(['hello'])
 
152
        b.add(['hello'], ['hello-id'])
 
153
        b.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
196
154
 
197
 
        tree.rename_one('hello', 'fruity')
198
 
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
155
        b.rename_one('hello', 'fruity')
 
156
        b.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
199
157
 
200
158
        eq = self.assertEquals
201
 
        tree1 = b.repository.revision_tree('test@rev-1')
202
 
        tree1.lock_read()
203
 
        self.addCleanup(tree1.unlock)
 
159
        tree1 = b.revision_tree('test@rev-1')
204
160
        eq(tree1.id2path('hello-id'), 'hello')
205
161
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
206
162
        self.assertFalse(tree1.has_filename('fruity'))
208
164
        ie = tree1.inventory['hello-id']
209
165
        eq(ie.revision, 'test@rev-1')
210
166
 
211
 
        tree2 = b.repository.revision_tree('test@rev-2')
212
 
        tree2.lock_read()
213
 
        self.addCleanup(tree2.unlock)
 
167
        tree2 = b.revision_tree('test@rev-2')
214
168
        eq(tree2.id2path('hello-id'), 'fruity')
215
169
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
216
170
        self.check_inventory_shape(tree2.inventory, ['fruity'])
217
171
        ie = tree2.inventory['hello-id']
218
172
        eq(ie.revision, 'test@rev-2')
219
173
 
 
174
 
220
175
    def test_reused_rev_id(self):
221
176
        """Test that a revision id cannot be reused in a branch"""
222
 
        wt = self.make_branch_and_tree('.')
223
 
        b = wt.branch
224
 
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
 
177
        b = Branch.initialize('.')
 
178
        b.commit('initial', rev_id='test@rev-1', allow_pointless=True)
225
179
        self.assertRaises(Exception,
226
 
                          wt.commit,
 
180
                          b.commit,
227
181
                          message='reused id',
228
182
                          rev_id='test@rev-1',
229
183
                          allow_pointless=True)
 
184
                          
 
185
 
230
186
 
231
187
    def test_commit_move(self):
232
188
        """Test commit of revisions with moved files and directories"""
233
189
        eq = self.assertEquals
234
 
        wt = self.make_branch_and_tree('.')
235
 
        b = wt.branch
 
190
        b = Branch.initialize('.')
236
191
        r1 = 'test@rev-1'
237
192
        self.build_tree(['hello', 'a/', 'b/'])
238
 
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
239
 
        wt.commit('initial', rev_id=r1, allow_pointless=False)
240
 
        wt.move(['hello'], 'a')
 
193
        b.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
 
194
        b.commit('initial', rev_id=r1, allow_pointless=False)
 
195
 
 
196
        b.move(['hello'], 'a')
241
197
        r2 = 'test@rev-2'
242
 
        wt.commit('two', rev_id=r2, allow_pointless=False)
243
 
        wt.lock_read()
244
 
        try:
245
 
            self.check_inventory_shape(wt.read_working_inventory(),
246
 
                                       ['a/', 'a/hello', 'b/'])
247
 
        finally:
248
 
            wt.unlock()
 
198
        b.commit('two', rev_id=r2, allow_pointless=False)
 
199
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
200
                                   ['a', 'a/hello', 'b'])
249
201
 
250
 
        wt.move(['b'], 'a')
 
202
        b.move(['b'], 'a')
251
203
        r3 = 'test@rev-3'
252
 
        wt.commit('three', rev_id=r3, allow_pointless=False)
253
 
        wt.lock_read()
254
 
        try:
255
 
            self.check_inventory_shape(wt.read_working_inventory(),
256
 
                                       ['a/', 'a/hello', 'a/b/'])
257
 
            self.check_inventory_shape(b.repository.get_revision_inventory(r3),
258
 
                                       ['a/', 'a/hello', 'a/b/'])
259
 
        finally:
260
 
            wt.unlock()
 
204
        b.commit('three', rev_id=r3, allow_pointless=False)
 
205
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
206
                                   ['a', 'a/hello', 'a/b'])
 
207
        self.check_inventory_shape(b.get_revision_inventory(r3),
 
208
                                   ['a', 'a/hello', 'a/b'])
261
209
 
262
 
        wt.move(['a/hello'], 'a/b')
 
210
        b.move([os.sep.join(['a', 'hello'])],
 
211
               os.sep.join(['a', 'b']))
263
212
        r4 = 'test@rev-4'
264
 
        wt.commit('four', rev_id=r4, allow_pointless=False)
265
 
        wt.lock_read()
266
 
        try:
267
 
            self.check_inventory_shape(wt.read_working_inventory(),
268
 
                                       ['a/', 'a/b/hello', 'a/b/'])
269
 
        finally:
270
 
            wt.unlock()
 
213
        b.commit('four', rev_id=r4, allow_pointless=False)
 
214
        self.check_inventory_shape(b.working_tree().read_working_inventory(),
 
215
                                   ['a', 'a/b/hello', 'a/b'])
271
216
 
272
 
        inv = b.repository.get_revision_inventory(r4)
 
217
        inv = b.get_revision_inventory(r4)
273
218
        eq(inv['hello-id'].revision, r4)
274
219
        eq(inv['a-id'].revision, r1)
275
220
        eq(inv['b-id'].revision, r3)
276
221
 
 
222
        
277
223
    def test_removed_commit(self):
278
224
        """Commit with a removed file"""
279
 
        wt = self.make_branch_and_tree('.')
280
 
        b = wt.branch
 
225
        b = Branch.initialize('.')
 
226
        wt = b.working_tree()
281
227
        file('hello', 'w').write('hello world')
282
 
        wt.add(['hello'], ['hello-id'])
283
 
        wt.commit(message='add hello')
 
228
        b.add(['hello'], ['hello-id'])
 
229
        b.commit(message='add hello')
 
230
 
 
231
        wt = b.working_tree()  # FIXME: kludge for aliasing of working inventory
284
232
        wt.remove('hello')
285
 
        wt.commit('removed hello', rev_id='rev2')
 
233
        b.commit('removed hello', rev_id='rev2')
286
234
 
287
 
        tree = b.repository.revision_tree('rev2')
 
235
        tree = b.revision_tree('rev2')
288
236
        self.assertFalse(tree.has_id('hello-id'))
289
237
 
 
238
 
290
239
    def test_committed_ancestry(self):
291
240
        """Test commit appends revisions to ancestry."""
292
 
        wt = self.make_branch_and_tree('.')
293
 
        b = wt.branch
 
241
        b = Branch.initialize('.')
294
242
        rev_ids = []
295
243
        for i in range(4):
296
244
            file('hello', 'w').write((str(i) * 4) + '\n')
297
245
            if i == 0:
298
 
                wt.add(['hello'], ['hello-id'])
 
246
                b.add(['hello'], ['hello-id'])
299
247
            rev_id = 'test@rev-%d' % (i+1)
300
248
            rev_ids.append(rev_id)
301
 
            wt.commit(message='rev %d' % (i+1),
 
249
            b.commit(message='rev %d' % (i+1),
302
250
                     rev_id=rev_id)
303
251
        eq = self.assertEquals
304
252
        eq(b.revision_history(), rev_ids)
305
253
        for i in range(4):
306
 
            anc = b.repository.get_ancestry(rev_ids[i])
 
254
            anc = b.get_ancestry(rev_ids[i])
307
255
            eq(anc, [None] + rev_ids[:i+1])
308
256
 
309
257
    def test_commit_new_subdir_child_selective(self):
310
 
        wt = self.make_branch_and_tree('.')
311
 
        b = wt.branch
 
258
        b = Branch.initialize('.')
312
259
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
313
 
        wt.add(['dir', 'dir/file1', 'dir/file2'],
 
260
        b.add(['dir', 'dir/file1', 'dir/file2'],
314
261
              ['dirid', 'file1id', 'file2id'])
315
 
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
316
 
        inv = b.repository.get_inventory('1')
 
262
        b.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
 
263
        inv = b.get_inventory('1')
317
264
        self.assertEqual('1', inv['dirid'].revision)
318
265
        self.assertEqual('1', inv['file1id'].revision)
319
266
        # FIXME: This should raise a KeyError I think, rbc20051006
322
269
    def test_strict_commit(self):
323
270
        """Try and commit with unknown files and strict = True, should fail."""
324
271
        from bzrlib.errors import StrictCommitFailed
325
 
        wt = self.make_branch_and_tree('.')
326
 
        b = wt.branch
 
272
        b = Branch.initialize('.')
327
273
        file('hello', 'w').write('hello world')
328
 
        wt.add('hello')
 
274
        b.add('hello')
329
275
        file('goodbye', 'w').write('goodbye cruel world!')
330
 
        self.assertRaises(StrictCommitFailed, wt.commit,
 
276
        self.assertRaises(StrictCommitFailed, b.commit,
331
277
            message='add hello but not goodbye', strict=True)
332
278
 
333
279
    def test_strict_commit_without_unknowns(self):
334
280
        """Try and commit with no unknown files and strict = True,
335
281
        should work."""
336
282
        from bzrlib.errors import StrictCommitFailed
337
 
        wt = self.make_branch_and_tree('.')
338
 
        b = wt.branch
 
283
        b = Branch.initialize('.')
339
284
        file('hello', 'w').write('hello world')
340
 
        wt.add('hello')
341
 
        wt.commit(message='add hello', strict=True)
 
285
        b.add('hello')
 
286
        b.commit(message='add hello', strict=True)
342
287
 
343
288
    def test_nonstrict_commit(self):
344
289
        """Try and commit with unknown files and strict = False, should work."""
345
 
        wt = self.make_branch_and_tree('.')
346
 
        b = wt.branch
 
290
        b = Branch.initialize('.')
347
291
        file('hello', 'w').write('hello world')
348
 
        wt.add('hello')
 
292
        b.add('hello')
349
293
        file('goodbye', 'w').write('goodbye cruel world!')
350
 
        wt.commit(message='add hello but not goodbye', strict=False)
 
294
        b.commit(message='add hello but not goodbye', strict=False)
351
295
 
352
296
    def test_nonstrict_commit_without_unknowns(self):
353
297
        """Try and commit with no unknown files and strict = False,
354
298
        should work."""
355
 
        wt = self.make_branch_and_tree('.')
356
 
        b = wt.branch
 
299
        b = Branch.initialize('.')
357
300
        file('hello', 'w').write('hello world')
358
 
        wt.add('hello')
359
 
        wt.commit(message='add hello', strict=False)
 
301
        b.add('hello')
 
302
        b.commit(message='add hello', strict=False)
360
303
 
361
304
    def test_signed_commit(self):
362
305
        import bzrlib.gpg
363
306
        import bzrlib.commit as commit
364
307
        oldstrategy = bzrlib.gpg.GPGStrategy
365
 
        wt = self.make_branch_and_tree('.')
366
 
        branch = wt.branch
367
 
        wt.commit("base", allow_pointless=True, rev_id='A')
368
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
308
        branch = Branch.initialize('.')
 
309
        branch.commit("base", allow_pointless=True, rev_id='A')
 
310
        self.failIf(branch.revision_store.has_id('A', 'sig'))
369
311
        try:
370
312
            from bzrlib.testament import Testament
371
313
            # monkey patch gpg signing mechanism
372
314
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
373
 
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
 
315
            commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
374
316
                                                      allow_pointless=True,
375
 
                                                      rev_id='B',
376
 
                                                      working_tree=wt)
377
 
            def sign(text):
378
 
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
379
 
            self.assertEqual(sign(Testament.from_revision(branch.repository,
380
 
                             'B').as_short_text()),
381
 
                             branch.repository.get_signature_text('B'))
 
317
                                                      rev_id='B')
 
318
            self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
 
319
                             branch.revision_store.get('B', 'sig').read())
382
320
        finally:
383
321
            bzrlib.gpg.GPGStrategy = oldstrategy
384
322
 
386
324
        import bzrlib.gpg
387
325
        import bzrlib.commit as commit
388
326
        oldstrategy = bzrlib.gpg.GPGStrategy
389
 
        wt = self.make_branch_and_tree('.')
390
 
        branch = wt.branch
391
 
        wt.commit("base", allow_pointless=True, rev_id='A')
392
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
327
        branch = Branch.initialize('.')
 
328
        branch.commit("base", allow_pointless=True, rev_id='A')
 
329
        self.failIf(branch.revision_store.has_id('A', 'sig'))
393
330
        try:
394
331
            from bzrlib.testament import Testament
395
332
            # monkey patch gpg signing mechanism
397
334
            config = MustSignConfig(branch)
398
335
            self.assertRaises(SigningFailed,
399
336
                              commit.Commit(config=config).commit,
400
 
                              message="base",
 
337
                              branch, "base",
401
338
                              allow_pointless=True,
402
 
                              rev_id='B',
403
 
                              working_tree=wt)
404
 
            branch = Branch.open(self.get_url('.'))
 
339
                              rev_id='B')
 
340
            branch = Branch.open('.')
405
341
            self.assertEqual(branch.revision_history(), ['A'])
406
 
            self.failIf(branch.repository.has_revision('B'))
 
342
            self.failIf(branch.revision_store.has_id('B'))
407
343
        finally:
408
344
            bzrlib.gpg.GPGStrategy = oldstrategy
409
345
 
410
346
    def test_commit_invokes_hooks(self):
411
347
        import bzrlib.commit as commit
412
 
        wt = self.make_branch_and_tree('.')
413
 
        branch = wt.branch
 
348
        branch = Branch.initialize('.')
414
349
        calls = []
415
350
        def called(branch, rev_id):
416
351
            calls.append('called')
418
353
        try:
419
354
            config = BranchWithHooks(branch)
420
355
            commit.Commit(config=config).commit(
421
 
                            message = "base",
 
356
                            branch, "base",
422
357
                            allow_pointless=True,
423
 
                            rev_id='A', working_tree = wt)
 
358
                            rev_id='A')
424
359
            self.assertEqual(['called', 'called'], calls)
425
360
        finally:
426
361
            del bzrlib.ahook
427
 
 
428
 
    def test_commit_object_doesnt_set_nick(self):
429
 
        # using the Commit object directly does not set the branch nick.
430
 
        wt = self.make_branch_and_tree('.')
431
 
        c = Commit()
432
 
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
433
 
        self.assertEquals(wt.branch.revno(), 1)
434
 
        self.assertEqual({},
435
 
                         wt.branch.repository.get_revision(
436
 
                            wt.branch.last_revision()).properties)
437
 
 
438
 
    def test_safe_master_lock(self):
439
 
        os.mkdir('master')
440
 
        master = BzrDirMetaFormat1().initialize('master')
441
 
        master.create_repository()
442
 
        master_branch = master.create_branch()
443
 
        master.create_workingtree()
444
 
        bound = master.sprout('bound')
445
 
        wt = bound.open_workingtree()
446
 
        wt.branch.set_bound_location(os.path.realpath('master'))
447
 
 
448
 
        orig_default = lockdir._DEFAULT_TIMEOUT_SECONDS
449
 
        master_branch.lock_write()
450
 
        try:
451
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = 1
452
 
            self.assertRaises(LockContention, wt.commit, 'silly')
453
 
        finally:
454
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_default
455
 
            master_branch.unlock()
456
 
 
457
 
    def test_commit_bound_merge(self):
458
 
        # see bug #43959; commit of a merge in a bound branch fails to push
459
 
        # the new commit into the master
460
 
        master_branch = self.make_branch('master')
461
 
        bound_tree = self.make_branch_and_tree('bound')
462
 
        bound_tree.branch.bind(master_branch)
463
 
 
464
 
        self.build_tree_contents([('bound/content_file', 'initial contents\n')])
465
 
        bound_tree.add(['content_file'])
466
 
        bound_tree.commit(message='woo!')
467
 
 
468
 
        other_bzrdir = master_branch.bzrdir.sprout('other')
469
 
        other_tree = other_bzrdir.open_workingtree()
470
 
 
471
 
        # do a commit to the the other branch changing the content file so
472
 
        # that our commit after merging will have a merged revision in the
473
 
        # content file history.
474
 
        self.build_tree_contents([('other/content_file', 'change in other\n')])
475
 
        other_tree.commit('change in other')
476
 
 
477
 
        # do a merge into the bound branch from other, and then change the
478
 
        # content file locally to force a new revision (rather than using the
479
 
        # revision from other). This forces extra processing in commit.
480
 
        bound_tree.merge_from_branch(other_tree.branch)
481
 
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
482
 
 
483
 
        # before #34959 was fixed, this failed with 'revision not present in
484
 
        # weave' when trying to implicitly push from the bound branch to the master
485
 
        bound_tree.commit(message='commit of merge in bound tree')
486
 
 
487
 
    def test_commit_reporting_after_merge(self):
488
 
        # when doing a commit of a merge, the reporter needs to still 
489
 
        # be called for each item that is added/removed/deleted.
490
 
        this_tree = self.make_branch_and_tree('this')
491
 
        # we need a bunch of files and dirs, to perform one action on each.
492
 
        self.build_tree([
493
 
            'this/dirtorename/',
494
 
            'this/dirtoreparent/',
495
 
            'this/dirtoleave/',
496
 
            'this/dirtoremove/',
497
 
            'this/filetoreparent',
498
 
            'this/filetorename',
499
 
            'this/filetomodify',
500
 
            'this/filetoremove',
501
 
            'this/filetoleave']
502
 
            )
503
 
        this_tree.add([
504
 
            'dirtorename',
505
 
            'dirtoreparent',
506
 
            'dirtoleave',
507
 
            'dirtoremove',
508
 
            'filetoreparent',
509
 
            'filetorename',
510
 
            'filetomodify',
511
 
            'filetoremove',
512
 
            'filetoleave']
513
 
            )
514
 
        this_tree.commit('create_files')
515
 
        other_dir = this_tree.bzrdir.sprout('other')
516
 
        other_tree = other_dir.open_workingtree()
517
 
        other_tree.lock_write()
518
 
        # perform the needed actions on the files and dirs.
519
 
        try:
520
 
            other_tree.rename_one('dirtorename', 'renameddir')
521
 
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
522
 
            other_tree.rename_one('filetorename', 'renamedfile')
523
 
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
524
 
            other_tree.remove(['dirtoremove', 'filetoremove'])
525
 
            self.build_tree_contents([
526
 
                ('other/newdir/', ),
527
 
                ('other/filetomodify', 'new content'),
528
 
                ('other/newfile', 'new file content')])
529
 
            other_tree.add('newfile')
530
 
            other_tree.add('newdir/')
531
 
            other_tree.commit('modify all sample files and dirs.')
532
 
        finally:
533
 
            other_tree.unlock()
534
 
        this_tree.merge_from_branch(other_tree.branch)
535
 
        reporter = CapturingReporter()
536
 
        this_tree.commit('do the commit', reporter=reporter)
537
 
        self.assertEqual([
538
 
            ('change', 'unchanged', ''),
539
 
            ('change', 'unchanged', 'dirtoleave'),
540
 
            ('change', 'unchanged', 'filetoleave'),
541
 
            ('change', 'modified', 'filetomodify'),
542
 
            ('change', 'added', 'newdir'),
543
 
            ('change', 'added', 'newfile'),
544
 
            ('renamed', 'renamed', 'dirtorename', 'renameddir'),
545
 
            ('renamed', 'renamed', 'filetorename', 'renamedfile'),
546
 
            ('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
547
 
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
548
 
            ('deleted', 'dirtoremove'),
549
 
            ('deleted', 'filetoremove'),
550
 
            ],
551
 
            reporter.calls)
552
 
 
553
 
    def test_commit_removals_respects_filespec(self):
554
 
        """Commit respects the specified_files for removals."""
555
 
        tree = self.make_branch_and_tree('.')
556
 
        self.build_tree(['a', 'b'])
557
 
        tree.add(['a', 'b'])
558
 
        tree.commit('added a, b')
559
 
        tree.remove(['a', 'b'])
560
 
        tree.commit('removed a', specific_files='a')
561
 
        basis = tree.basis_tree()
562
 
        tree.lock_read()
563
 
        try:
564
 
            self.assertIs(None, basis.path2id('a'))
565
 
            self.assertFalse(basis.path2id('b') is None)
566
 
        finally:
567
 
            tree.unlock()
568
 
 
569
 
    def test_commit_saves_1ms_timestamp(self):
570
 
        """Passing in a timestamp is saved with 1ms resolution"""
571
 
        tree = self.make_branch_and_tree('.')
572
 
        self.build_tree(['a'])
573
 
        tree.add('a')
574
 
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
575
 
                    rev_id='a1')
576
 
 
577
 
        rev = tree.branch.repository.get_revision('a1')
578
 
        self.assertEqual(1153248633.419, rev.timestamp)
579
 
 
580
 
    def test_commit_has_1ms_resolution(self):
581
 
        """Allowing commit to generate the timestamp also has 1ms resolution"""
582
 
        tree = self.make_branch_and_tree('.')
583
 
        self.build_tree(['a'])
584
 
        tree.add('a')
585
 
        tree.commit('added a', rev_id='a1')
586
 
 
587
 
        rev = tree.branch.repository.get_revision('a1')
588
 
        timestamp = rev.timestamp
589
 
        timestamp_1ms = round(timestamp, 3)
590
 
        self.assertEqual(timestamp_1ms, timestamp)
591
 
 
592
 
    def assertBasisTreeKind(self, kind, tree, file_id):
593
 
        basis = tree.basis_tree()
594
 
        basis.lock_read()
595
 
        try:
596
 
            self.assertEqual(kind, basis.kind(file_id))
597
 
        finally:
598
 
            basis.unlock()
599
 
 
600
 
    def test_commit_kind_changes(self):
601
 
        self.requireFeature(SymlinkFeature)
602
 
        tree = self.make_branch_and_tree('.')
603
 
        os.symlink('target', 'name')
604
 
        tree.add('name', 'a-file-id')
605
 
        tree.commit('Added a symlink')
606
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
607
 
 
608
 
        os.unlink('name')
609
 
        self.build_tree(['name'])
610
 
        tree.commit('Changed symlink to file')
611
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
612
 
 
613
 
        os.unlink('name')
614
 
        os.symlink('target', 'name')
615
 
        tree.commit('file to symlink')
616
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
617
 
 
618
 
        os.unlink('name')
619
 
        os.mkdir('name')
620
 
        tree.commit('symlink to directory')
621
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
622
 
 
623
 
        os.rmdir('name')
624
 
        os.symlink('target', 'name')
625
 
        tree.commit('directory to symlink')
626
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
627
 
 
628
 
        # prepare for directory <-> file tests
629
 
        os.unlink('name')
630
 
        os.mkdir('name')
631
 
        tree.commit('symlink to directory')
632
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
633
 
 
634
 
        os.rmdir('name')
635
 
        self.build_tree(['name'])
636
 
        tree.commit('Changed directory to file')
637
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
638
 
 
639
 
        os.unlink('name')
640
 
        os.mkdir('name')
641
 
        tree.commit('file to directory')
642
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
643
 
 
644
 
    def test_commit_unversioned_specified(self):
645
 
        """Commit should raise if specified files isn't in basis or worktree"""
646
 
        tree = self.make_branch_and_tree('.')
647
 
        self.assertRaises(errors.PathsNotVersionedError, tree.commit, 
648
 
                          'message', specific_files=['bogus'])
649
 
 
650
 
    class Callback(object):
651
 
        
652
 
        def __init__(self, message, testcase):
653
 
            self.called = False
654
 
            self.message = message
655
 
            self.testcase = testcase
656
 
 
657
 
        def __call__(self, commit_obj):
658
 
            self.called = True
659
 
            self.testcase.assertTrue(isinstance(commit_obj, Commit))
660
 
            return self.message
661
 
 
662
 
    def test_commit_callback(self):
663
 
        """Commit should invoke a callback to get the message"""
664
 
 
665
 
        tree = self.make_branch_and_tree('.')
666
 
        try:
667
 
            tree.commit()
668
 
        except Exception, e:
669
 
            self.assertTrue(isinstance(e, BzrError))
670
 
            self.assertEqual('The message or message_callback keyword'
671
 
                             ' parameter is required for commit().', str(e))
672
 
        else:
673
 
            self.fail('exception not raised')
674
 
        cb = self.Callback(u'commit 1', self)
675
 
        tree.commit(message_callback=cb)
676
 
        self.assertTrue(cb.called)
677
 
        repository = tree.branch.repository
678
 
        message = repository.get_revision(tree.last_revision()).message
679
 
        self.assertEqual('commit 1', message)
680
 
 
681
 
    def test_no_callback_pointless(self):
682
 
        """Callback should not be invoked for pointless commit"""
683
 
        tree = self.make_branch_and_tree('.')
684
 
        cb = self.Callback(u'commit 2', self)
685
 
        self.assertRaises(PointlessCommit, tree.commit, message_callback=cb, 
686
 
                          allow_pointless=False)
687
 
        self.assertFalse(cb.called)
688
 
 
689
 
    def test_no_callback_netfailure(self):
690
 
        """Callback should not be invoked if connectivity fails"""
691
 
        tree = self.make_branch_and_tree('.')
692
 
        cb = self.Callback(u'commit 2', self)
693
 
        repository = tree.branch.repository
694
 
        # simulate network failure
695
 
        def raise_(self, arg, arg2):
696
 
            raise errors.NoSuchFile('foo')
697
 
        repository.add_inventory = raise_
698
 
        self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
699
 
        self.assertFalse(cb.called)
700
 
 
701
 
    def test_selected_file_merge_commit(self):
702
 
        """Ensure the correct error is raised"""
703
 
        tree = self.make_branch_and_tree('foo')
704
 
        # pending merge would turn into a left parent
705
 
        tree.commit('commit 1')
706
 
        tree.add_parent_tree_id('example')
707
 
        self.build_tree(['foo/bar', 'foo/baz'])
708
 
        tree.add(['bar', 'baz'])
709
 
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
710
 
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
711
 
        self.assertEqual(['bar', 'baz'], err.files)
712
 
        self.assertEqual('Selected-file commit of merges is not supported'
713
 
                         ' yet: files bar, baz', str(err))
714
 
 
715
 
    def test_commit_ordering(self):
716
 
        """Test of corner-case commit ordering error"""
717
 
        tree = self.make_branch_and_tree('.')
718
 
        self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
719
 
        tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
720
 
        tree.commit('setup')
721
 
        self.build_tree(['a/c/d/'])
722
 
        tree.add('a/c/d')
723
 
        tree.rename_one('a/z/x', 'a/c/d/x')
724
 
        tree.commit('test', specific_files=['a/z/y'])
725
 
 
726
 
    def test_commit_no_author(self):
727
 
        """The default kwarg author in MutableTree.commit should not add
728
 
        the 'author' revision property.
729
 
        """
730
 
        tree = self.make_branch_and_tree('foo')
731
 
        rev_id = tree.commit('commit 1')
732
 
        rev = tree.branch.repository.get_revision(rev_id)
733
 
        self.assertFalse('author' in rev.properties)
734
 
 
735
 
    def test_commit_author(self):
736
 
        """Passing a non-empty author kwarg to MutableTree.commit should add
737
 
        the 'author' revision property.
738
 
        """
739
 
        tree = self.make_branch_and_tree('foo')
740
 
        rev_id = tree.commit('commit 1', author='John Doe <jdoe@example.com>')
741
 
        rev = tree.branch.repository.get_revision(rev_id)
742
 
        self.assertEqual('John Doe <jdoe@example.com>',
743
 
                         rev.properties['author'])
744
 
 
745
 
    def test_commit_with_checkout_and_branch_sharing_repo(self):
746
 
        repo = self.make_repository('repo', shared=True)
747
 
        # make_branch_and_tree ignores shared repos
748
 
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
749
 
        tree2 = branch.create_checkout('repo/tree2')
750
 
        tree2.commit('message', rev_id='rev1')
751
 
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))