~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_commit.py

- avoid warning about log not being registered during startup

Show diffs side-by-side

added added

removed removed

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