~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Aaron Bentley
  • Date: 2006-11-10 01:55:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2127.
  • Revision ID: aaron.bentley@utoronto.ca-20061110015555-f48202744b630209
Ignore html docs (both kinds)

Show diffs side-by-side

added added

removed removed

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