~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_commit.py

Fix BzrDir.create_workingtree for NULL_REVISION

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