~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

  • Committer: Robert Collins
  • Date: 2006-09-25 00:03:15 UTC
  • mto: This revision was merged to the branch mainline in revision 2038.
  • Revision ID: robertc@robertcollins.net-20060925000315-d096352885e1b2ec
(Robert Collins) bzr 0.11rc1 has branch, bump bzr.dev version to 0.12

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