/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_commit.py

Fix DeprecationWarning in RemoteRepository.get_rev_id_for_revno.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
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
16
16
 
17
17
 
18
18
import os
 
19
from io import BytesIO
19
20
 
20
 
import bzrlib
21
 
from bzrlib import (
22
 
    bzrdir,
 
21
import breezy
 
22
from .. import (
 
23
    config,
 
24
    controldir,
23
25
    errors,
24
 
    lockdir,
25
 
    osutils,
26
 
    tests,
27
 
    )
28
 
from bzrlib.branch import Branch
29
 
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
30
 
from bzrlib.commit import Commit, NullCommitReporter
31
 
from bzrlib.config import BranchConfig
32
 
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
33
 
                           LockContention)
34
 
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
35
 
from bzrlib.workingtree import WorkingTree
 
26
    trace,
 
27
    )
 
28
from ..branch import Branch
 
29
from ..bzr.bzrdir import BzrDirMetaFormat1
 
30
from ..commit import (
 
31
    CannotCommitSelectedFileMerge,
 
32
    Commit,
 
33
    NullCommitReporter,
 
34
    PointlessCommit,
 
35
    filter_excluded,
 
36
    )
 
37
from ..errors import (
 
38
    BzrError,
 
39
    LockContention,
 
40
    )
 
41
from . import (
 
42
    TestCase,
 
43
    TestCaseWithTransport,
 
44
    test_foreign,
 
45
    )
 
46
from .features import (
 
47
    SymlinkFeature,
 
48
    )
 
49
from .matchers import MatchesAncestry
36
50
 
37
51
 
38
52
# TODO: Test commit with some added, and added-but-missing files
39
53
 
40
 
class MustSignConfig(BranchConfig):
41
 
 
42
 
    def signature_needed(self):
43
 
        return True
44
 
 
45
 
    def gpg_signing_command(self):
46
 
        return ['cat', '-']
47
 
 
48
 
 
49
 
class BranchWithHooks(BranchConfig):
50
 
 
51
 
    def post_commit(self):
52
 
        return "bzrlib.ahook bzrlib.ahook"
 
54
class MustSignConfig(config.MemoryStack):
 
55
 
 
56
    def __init__(self):
 
57
        super(MustSignConfig, self).__init__(b'''
 
58
create_signatures=always
 
59
''')
53
60
 
54
61
 
55
62
class CapturingReporter(NullCommitReporter):
81
88
        """Commit and check two versions of a single file."""
82
89
        wt = self.make_branch_and_tree('.')
83
90
        b = wt.branch
84
 
        file('hello', 'w').write('hello world')
 
91
        with open('hello', 'w') as f:
 
92
            f.write('hello world')
85
93
        wt.add('hello')
86
 
        wt.commit(message='add hello')
87
 
        file_id = wt.path2id('hello')
88
 
 
89
 
        file('hello', 'w').write('version 2')
90
 
        wt.commit(message='commit 2')
91
 
 
92
 
        eq = self.assertEquals
 
94
        rev1 = wt.commit(message='add hello')
 
95
 
 
96
        with open('hello', 'w') as f:
 
97
            f.write('version 2')
 
98
        rev2 = wt.commit(message='commit 2')
 
99
 
 
100
        eq = self.assertEqual
93
101
        eq(b.revno(), 2)
94
 
        rh = b.revision_history()
95
 
        rev = b.repository.get_revision(rh[0])
 
102
        rev = b.repository.get_revision(rev1)
96
103
        eq(rev.message, 'add hello')
97
104
 
98
 
        tree1 = b.repository.revision_tree(rh[0])
 
105
        tree1 = b.repository.revision_tree(rev1)
99
106
        tree1.lock_read()
100
 
        text = tree1.get_file_text(file_id)
 
107
        text = tree1.get_file_text('hello')
101
108
        tree1.unlock()
102
 
        self.assertEqual('hello world', text)
 
109
        self.assertEqual(b'hello world', text)
103
110
 
104
 
        tree2 = b.repository.revision_tree(rh[1])
 
111
        tree2 = b.repository.revision_tree(rev2)
105
112
        tree2.lock_read()
106
 
        text = tree2.get_file_text(file_id)
 
113
        text = tree2.get_file_text('hello')
107
114
        tree2.unlock()
108
 
        self.assertEqual('version 2', text)
 
115
        self.assertEqual(b'version 2', text)
 
116
 
 
117
    def test_commit_lossy_native(self):
 
118
        """Attempt a lossy commit to a native branch."""
 
119
        wt = self.make_branch_and_tree('.')
 
120
        b = wt.branch
 
121
        with open('hello', 'w') as f:
 
122
            f.write('hello world')
 
123
        wt.add('hello')
 
124
        revid = wt.commit(message='add hello', rev_id=b'revid', lossy=True)
 
125
        self.assertEqual(b'revid', revid)
 
126
 
 
127
    def test_commit_lossy_foreign(self):
 
128
        """Attempt a lossy commit to a foreign branch."""
 
129
        test_foreign.register_dummy_foreign_for_test(self)
 
130
        wt = self.make_branch_and_tree('.',
 
131
                                       format=test_foreign.DummyForeignVcsDirFormat())
 
132
        b = wt.branch
 
133
        with open('hello', 'w') as f:
 
134
            f.write('hello world')
 
135
        wt.add('hello')
 
136
        revid = wt.commit(message='add hello', lossy=True,
 
137
                          timestamp=1302659388, timezone=0)
 
138
        self.assertEqual(b'dummy-v1:1302659388-0-UNKNOWN', revid)
 
139
 
 
140
    def test_commit_bound_lossy_foreign(self):
 
141
        """Attempt a lossy commit to a bzr branch bound to a foreign branch."""
 
142
        test_foreign.register_dummy_foreign_for_test(self)
 
143
        foreign_branch = self.make_branch('foreign',
 
144
                                          format=test_foreign.DummyForeignVcsDirFormat())
 
145
        wt = foreign_branch.create_checkout("local")
 
146
        b = wt.branch
 
147
        with open('local/hello', 'w') as f:
 
148
            f.write('hello world')
 
149
        wt.add('hello')
 
150
        revid = wt.commit(message='add hello', lossy=True,
 
151
                          timestamp=1302659388, timezone=0)
 
152
        self.assertEqual(b'dummy-v1:1302659388-0-0', revid)
 
153
        self.assertEqual(b'dummy-v1:1302659388-0-0',
 
154
                         foreign_branch.last_revision())
 
155
        self.assertEqual(b'dummy-v1:1302659388-0-0',
 
156
                         wt.branch.last_revision())
109
157
 
110
158
    def test_missing_commit(self):
111
159
        """Test a commit with a missing file"""
112
160
        wt = self.make_branch_and_tree('.')
113
161
        b = wt.branch
114
 
        file('hello', 'w').write('hello world')
115
 
        wt.add(['hello'], ['hello-id'])
 
162
        with open('hello', 'w') as f:
 
163
            f.write('hello world')
 
164
        wt.add(['hello'], [b'hello-id'])
116
165
        wt.commit(message='add hello')
117
166
 
118
167
        os.remove('hello')
119
 
        wt.commit('removed hello', rev_id='rev2')
 
168
        reporter = CapturingReporter()
 
169
        wt.commit('removed hello', rev_id=b'rev2', reporter=reporter)
 
170
        self.assertEqual(
 
171
            [('missing', u'hello'), ('deleted', u'hello')],
 
172
            reporter.calls)
120
173
 
121
 
        tree = b.repository.revision_tree('rev2')
122
 
        self.assertFalse(tree.has_id('hello-id'))
 
174
        tree = b.repository.revision_tree(b'rev2')
 
175
        self.assertFalse(tree.has_id(b'hello-id'))
123
176
 
124
177
    def test_partial_commit_move(self):
125
178
        """Test a partial commit where a file was renamed but not committed.
134
187
        b = wt.branch
135
188
        self.build_tree(['annotate/', 'annotate/foo.py',
136
189
                         'olive/', 'olive/dialog.py'
137
 
                        ])
 
190
                         ])
138
191
        wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
139
192
        wt.commit(message='add files')
140
193
        wt.rename_one("olive/dialog.py", "aaa")
141
 
        self.build_tree_contents([('annotate/foo.py', 'modified\n')])
 
194
        self.build_tree_contents([('annotate/foo.py', b'modified\n')])
142
195
        wt.commit('renamed hello', specific_files=["annotate"])
143
196
 
144
197
    def test_pointless_commit(self):
145
198
        """Commit refuses unless there are changes or it's forced."""
146
199
        wt = self.make_branch_and_tree('.')
147
200
        b = wt.branch
148
 
        file('hello', 'w').write('hello')
 
201
        with open('hello', 'w') as f:
 
202
            f.write('hello')
149
203
        wt.add(['hello'])
150
204
        wt.commit(message='add hello')
151
 
        self.assertEquals(b.revno(), 1)
 
205
        self.assertEqual(b.revno(), 1)
152
206
        self.assertRaises(PointlessCommit,
153
207
                          wt.commit,
154
208
                          message='fails',
155
209
                          allow_pointless=False)
156
 
        self.assertEquals(b.revno(), 1)
 
210
        self.assertEqual(b.revno(), 1)
157
211
 
158
212
    def test_commit_empty(self):
159
213
        """Commiting an empty tree works."""
165
219
                          message='empty tree',
166
220
                          allow_pointless=False)
167
221
        wt.commit(message='empty tree', allow_pointless=True)
168
 
        self.assertEquals(b.revno(), 2)
 
222
        self.assertEqual(b.revno(), 2)
169
223
 
170
224
    def test_selective_delete(self):
171
225
        """Selective commit in tree with deletions"""
172
226
        wt = self.make_branch_and_tree('.')
173
227
        b = wt.branch
174
 
        file('hello', 'w').write('hello')
175
 
        file('buongia', 'w').write('buongia')
 
228
        with open('hello', 'w') as f:
 
229
            f.write('hello')
 
230
        with open('buongia', 'w') as f:
 
231
            f.write('buongia')
176
232
        wt.add(['hello', 'buongia'],
177
 
              ['hello-id', 'buongia-id'])
 
233
               [b'hello-id', b'buongia-id'])
178
234
        wt.commit(message='add files',
179
 
                 rev_id='test@rev-1')
 
235
                  rev_id=b'test@rev-1')
180
236
 
181
237
        os.remove('hello')
182
 
        file('buongia', 'w').write('new text')
 
238
        with open('buongia', 'w') as f:
 
239
            f.write('new text')
183
240
        wt.commit(message='update text',
184
 
                 specific_files=['buongia'],
185
 
                 allow_pointless=False,
186
 
                 rev_id='test@rev-2')
 
241
                  specific_files=['buongia'],
 
242
                  allow_pointless=False,
 
243
                  rev_id=b'test@rev-2')
187
244
 
188
245
        wt.commit(message='remove hello',
189
 
                 specific_files=['hello'],
190
 
                 allow_pointless=False,
191
 
                 rev_id='test@rev-3')
 
246
                  specific_files=['hello'],
 
247
                  allow_pointless=False,
 
248
                  rev_id=b'test@rev-3')
192
249
 
193
 
        eq = self.assertEquals
 
250
        eq = self.assertEqual
194
251
        eq(b.revno(), 3)
195
252
 
196
 
        tree2 = b.repository.revision_tree('test@rev-2')
 
253
        tree2 = b.repository.revision_tree(b'test@rev-2')
197
254
        tree2.lock_read()
198
255
        self.addCleanup(tree2.unlock)
199
256
        self.assertTrue(tree2.has_filename('hello'))
200
 
        self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
201
 
        self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
 
257
        self.assertEqual(tree2.get_file_text('hello'), b'hello')
 
258
        self.assertEqual(tree2.get_file_text('buongia'), b'new text')
202
259
 
203
 
        tree3 = b.repository.revision_tree('test@rev-3')
 
260
        tree3 = b.repository.revision_tree(b'test@rev-3')
204
261
        tree3.lock_read()
205
262
        self.addCleanup(tree3.unlock)
206
263
        self.assertFalse(tree3.has_filename('hello'))
207
 
        self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
 
264
        self.assertEqual(tree3.get_file_text('buongia'), b'new text')
208
265
 
209
266
    def test_commit_rename(self):
210
267
        """Test commit of a revision where a file is renamed."""
211
268
        tree = self.make_branch_and_tree('.')
212
269
        b = tree.branch
213
270
        self.build_tree(['hello'], line_endings='binary')
214
 
        tree.add(['hello'], ['hello-id'])
215
 
        tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
 
271
        tree.add(['hello'], [b'hello-id'])
 
272
        tree.commit(message='one', rev_id=b'test@rev-1', allow_pointless=False)
216
273
 
217
274
        tree.rename_one('hello', 'fruity')
218
 
        tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
 
275
        tree.commit(message='renamed', rev_id=b'test@rev-2',
 
276
                    allow_pointless=False)
219
277
 
220
 
        eq = self.assertEquals
221
 
        tree1 = b.repository.revision_tree('test@rev-1')
 
278
        eq = self.assertEqual
 
279
        tree1 = b.repository.revision_tree(b'test@rev-1')
222
280
        tree1.lock_read()
223
281
        self.addCleanup(tree1.unlock)
224
 
        eq(tree1.id2path('hello-id'), 'hello')
225
 
        eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
 
282
        eq(tree1.id2path(b'hello-id'), 'hello')
 
283
        eq(tree1.get_file_text('hello'), b'contents of hello\n')
226
284
        self.assertFalse(tree1.has_filename('fruity'))
227
 
        self.check_inventory_shape(tree1.inventory, ['hello'])
228
 
        ie = tree1.inventory['hello-id']
229
 
        eq(ie.revision, 'test@rev-1')
 
285
        self.check_tree_shape(tree1, ['hello'])
 
286
        eq(tree1.get_file_revision('hello'), b'test@rev-1')
230
287
 
231
 
        tree2 = b.repository.revision_tree('test@rev-2')
 
288
        tree2 = b.repository.revision_tree(b'test@rev-2')
232
289
        tree2.lock_read()
233
290
        self.addCleanup(tree2.unlock)
234
 
        eq(tree2.id2path('hello-id'), 'fruity')
235
 
        eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
236
 
        self.check_inventory_shape(tree2.inventory, ['fruity'])
237
 
        ie = tree2.inventory['hello-id']
238
 
        eq(ie.revision, 'test@rev-2')
 
291
        eq(tree2.id2path(b'hello-id'), 'fruity')
 
292
        eq(tree2.get_file_text('fruity'), b'contents of hello\n')
 
293
        self.check_tree_shape(tree2, ['fruity'])
 
294
        eq(tree2.get_file_revision('fruity'), b'test@rev-2')
239
295
 
240
296
    def test_reused_rev_id(self):
241
297
        """Test that a revision id cannot be reused in a branch"""
242
298
        wt = self.make_branch_and_tree('.')
243
299
        b = wt.branch
244
 
        wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
 
300
        wt.commit('initial', rev_id=b'test@rev-1', allow_pointless=True)
245
301
        self.assertRaises(Exception,
246
302
                          wt.commit,
247
303
                          message='reused id',
248
 
                          rev_id='test@rev-1',
 
304
                          rev_id=b'test@rev-1',
249
305
                          allow_pointless=True)
250
306
 
251
307
    def test_commit_move(self):
252
308
        """Test commit of revisions with moved files and directories"""
253
 
        eq = self.assertEquals
 
309
        eq = self.assertEqual
254
310
        wt = self.make_branch_and_tree('.')
255
311
        b = wt.branch
256
 
        r1 = 'test@rev-1'
 
312
        r1 = b'test@rev-1'
257
313
        self.build_tree(['hello', 'a/', 'b/'])
258
 
        wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
 
314
        wt.add(['hello', 'a', 'b'], [b'hello-id', b'a-id', b'b-id'])
259
315
        wt.commit('initial', rev_id=r1, allow_pointless=False)
260
316
        wt.move(['hello'], 'a')
261
 
        r2 = 'test@rev-2'
 
317
        r2 = b'test@rev-2'
262
318
        wt.commit('two', rev_id=r2, allow_pointless=False)
263
319
        wt.lock_read()
264
320
        try:
265
 
            self.check_inventory_shape(wt.read_working_inventory(),
266
 
                                       ['a/', 'a/hello', 'b/'])
 
321
            self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
267
322
        finally:
268
323
            wt.unlock()
269
324
 
270
325
        wt.move(['b'], 'a')
271
 
        r3 = 'test@rev-3'
 
326
        r3 = b'test@rev-3'
272
327
        wt.commit('three', rev_id=r3, allow_pointless=False)
273
328
        wt.lock_read()
274
329
        try:
275
 
            self.check_inventory_shape(wt.read_working_inventory(),
276
 
                                       ['a/', 'a/hello', 'a/b/'])
277
 
            self.check_inventory_shape(b.repository.get_inventory(r3),
278
 
                                       ['a/', 'a/hello', 'a/b/'])
 
330
            self.check_tree_shape(wt,
 
331
                                  ['a/', 'a/hello', 'a/b/'])
 
332
            self.check_tree_shape(b.repository.revision_tree(r3),
 
333
                                  ['a/', 'a/hello', 'a/b/'])
279
334
        finally:
280
335
            wt.unlock()
281
336
 
282
337
        wt.move(['a/hello'], 'a/b')
283
 
        r4 = 'test@rev-4'
 
338
        r4 = b'test@rev-4'
284
339
        wt.commit('four', rev_id=r4, allow_pointless=False)
285
340
        wt.lock_read()
286
341
        try:
287
 
            self.check_inventory_shape(wt.read_working_inventory(),
288
 
                                       ['a/', 'a/b/hello', 'a/b/'])
 
342
            self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
289
343
        finally:
290
344
            wt.unlock()
291
345
 
292
346
        inv = b.repository.get_inventory(r4)
293
 
        eq(inv['hello-id'].revision, r4)
294
 
        eq(inv['a-id'].revision, r1)
295
 
        eq(inv['b-id'].revision, r3)
 
347
        eq(inv.get_entry(b'hello-id').revision, r4)
 
348
        eq(inv.get_entry(b'a-id').revision, r1)
 
349
        eq(inv.get_entry(b'b-id').revision, r3)
296
350
 
297
351
    def test_removed_commit(self):
298
352
        """Commit with a removed file"""
299
353
        wt = self.make_branch_and_tree('.')
300
354
        b = wt.branch
301
 
        file('hello', 'w').write('hello world')
302
 
        wt.add(['hello'], ['hello-id'])
 
355
        with open('hello', 'w') as f:
 
356
            f.write('hello world')
 
357
        wt.add(['hello'], [b'hello-id'])
303
358
        wt.commit(message='add hello')
304
359
        wt.remove('hello')
305
 
        wt.commit('removed hello', rev_id='rev2')
 
360
        wt.commit('removed hello', rev_id=b'rev2')
306
361
 
307
 
        tree = b.repository.revision_tree('rev2')
308
 
        self.assertFalse(tree.has_id('hello-id'))
 
362
        tree = b.repository.revision_tree(b'rev2')
 
363
        self.assertFalse(tree.has_id(b'hello-id'))
309
364
 
310
365
    def test_committed_ancestry(self):
311
366
        """Test commit appends revisions to ancestry."""
313
368
        b = wt.branch
314
369
        rev_ids = []
315
370
        for i in range(4):
316
 
            file('hello', 'w').write((str(i) * 4) + '\n')
 
371
            with open('hello', 'w') as f:
 
372
                f.write((str(i) * 4) + '\n')
317
373
            if i == 0:
318
 
                wt.add(['hello'], ['hello-id'])
319
 
            rev_id = 'test@rev-%d' % (i+1)
 
374
                wt.add(['hello'], [b'hello-id'])
 
375
            rev_id = b'test@rev-%d' % (i + 1)
320
376
            rev_ids.append(rev_id)
321
 
            wt.commit(message='rev %d' % (i+1),
322
 
                     rev_id=rev_id)
323
 
        eq = self.assertEquals
324
 
        eq(b.revision_history(), rev_ids)
 
377
            wt.commit(message='rev %d' % (i + 1),
 
378
                      rev_id=rev_id)
325
379
        for i in range(4):
326
 
            anc = b.repository.get_ancestry(rev_ids[i])
327
 
            eq(anc, [None] + rev_ids[:i+1])
 
380
            self.assertThat(rev_ids[:i + 1],
 
381
                            MatchesAncestry(b.repository, rev_ids[i]))
328
382
 
329
383
    def test_commit_new_subdir_child_selective(self):
330
384
        wt = self.make_branch_and_tree('.')
331
385
        b = wt.branch
332
386
        self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
333
387
        wt.add(['dir', 'dir/file1', 'dir/file2'],
334
 
              ['dirid', 'file1id', 'file2id'])
335
 
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
336
 
        inv = b.repository.get_inventory('1')
337
 
        self.assertEqual('1', inv['dirid'].revision)
338
 
        self.assertEqual('1', inv['file1id'].revision)
 
388
               [b'dirid', b'file1id', b'file2id'])
 
389
        wt.commit('dir/file1', specific_files=['dir/file1'], rev_id=b'1')
 
390
        inv = b.repository.get_inventory(b'1')
 
391
        self.assertEqual(b'1', inv.get_entry(b'dirid').revision)
 
392
        self.assertEqual(b'1', inv.get_entry(b'file1id').revision)
339
393
        # FIXME: This should raise a KeyError I think, rbc20051006
340
 
        self.assertRaises(BzrError, inv.__getitem__, 'file2id')
 
394
        self.assertRaises(BzrError, inv.get_entry, b'file2id')
341
395
 
342
396
    def test_strict_commit(self):
343
397
        """Try and commit with unknown files and strict = True, should fail."""
344
 
        from bzrlib.errors import StrictCommitFailed
 
398
        from ..errors import StrictCommitFailed
345
399
        wt = self.make_branch_and_tree('.')
346
400
        b = wt.branch
347
 
        file('hello', 'w').write('hello world')
 
401
        with open('hello', 'w') as f:
 
402
            f.write('hello world')
348
403
        wt.add('hello')
349
 
        file('goodbye', 'w').write('goodbye cruel world!')
 
404
        with open('goodbye', 'w') as f:
 
405
            f.write('goodbye cruel world!')
350
406
        self.assertRaises(StrictCommitFailed, wt.commit,
351
 
            message='add hello but not goodbye', strict=True)
 
407
                          message='add hello but not goodbye', strict=True)
352
408
 
353
409
    def test_strict_commit_without_unknowns(self):
354
410
        """Try and commit with no unknown files and strict = True,
355
411
        should work."""
356
 
        from bzrlib.errors import StrictCommitFailed
357
412
        wt = self.make_branch_and_tree('.')
358
413
        b = wt.branch
359
 
        file('hello', 'w').write('hello world')
 
414
        with open('hello', 'w') as f:
 
415
            f.write('hello world')
360
416
        wt.add('hello')
361
417
        wt.commit(message='add hello', strict=True)
362
418
 
364
420
        """Try and commit with unknown files and strict = False, should work."""
365
421
        wt = self.make_branch_and_tree('.')
366
422
        b = wt.branch
367
 
        file('hello', 'w').write('hello world')
 
423
        with open('hello', 'w') as f:
 
424
            f.write('hello world')
368
425
        wt.add('hello')
369
 
        file('goodbye', 'w').write('goodbye cruel world!')
 
426
        with open('goodbye', 'w') as f:
 
427
            f.write('goodbye cruel world!')
370
428
        wt.commit(message='add hello but not goodbye', strict=False)
371
429
 
372
430
    def test_nonstrict_commit_without_unknowns(self):
374
432
        should work."""
375
433
        wt = self.make_branch_and_tree('.')
376
434
        b = wt.branch
377
 
        file('hello', 'w').write('hello world')
 
435
        with open('hello', 'w') as f:
 
436
            f.write('hello world')
378
437
        wt.add('hello')
379
438
        wt.commit(message='add hello', strict=False)
380
439
 
381
440
    def test_signed_commit(self):
382
 
        import bzrlib.gpg
383
 
        import bzrlib.commit as commit
384
 
        oldstrategy = bzrlib.gpg.GPGStrategy
 
441
        import breezy.gpg
 
442
        import breezy.commit as commit
 
443
        oldstrategy = breezy.gpg.GPGStrategy
385
444
        wt = self.make_branch_and_tree('.')
386
445
        branch = wt.branch
387
 
        wt.commit("base", allow_pointless=True, rev_id='A')
388
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
446
        wt.commit("base", allow_pointless=True, rev_id=b'A')
 
447
        self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
389
448
        try:
390
 
            from bzrlib.testament import Testament
 
449
            from ..bzr.testament import Testament
391
450
            # monkey patch gpg signing mechanism
392
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
393
 
            commit.Commit(config=MustSignConfig(branch)).commit(message="base",
394
 
                                                      allow_pointless=True,
395
 
                                                      rev_id='B',
396
 
                                                      working_tree=wt)
 
451
            breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
 
452
            conf = config.MemoryStack(b'''
 
453
create_signatures=always
 
454
''')
 
455
            commit.Commit(config_stack=conf).commit(
 
456
                message="base", allow_pointless=True, rev_id=b'B',
 
457
                working_tree=wt)
 
458
 
397
459
            def sign(text):
398
 
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
460
                return breezy.gpg.LoopbackGPGStrategy(None).sign(
 
461
                    text, breezy.gpg.MODE_CLEAR)
399
462
            self.assertEqual(sign(Testament.from_revision(branch.repository,
400
 
                             'B').as_short_text()),
401
 
                             branch.repository.get_signature_text('B'))
 
463
                                                          b'B').as_short_text()),
 
464
                             branch.repository.get_signature_text(b'B'))
402
465
        finally:
403
 
            bzrlib.gpg.GPGStrategy = oldstrategy
 
466
            breezy.gpg.GPGStrategy = oldstrategy
404
467
 
405
468
    def test_commit_failed_signature(self):
406
 
        import bzrlib.gpg
407
 
        import bzrlib.commit as commit
408
 
        oldstrategy = bzrlib.gpg.GPGStrategy
 
469
        import breezy.gpg
 
470
        import breezy.commit as commit
 
471
        oldstrategy = breezy.gpg.GPGStrategy
409
472
        wt = self.make_branch_and_tree('.')
410
473
        branch = wt.branch
411
 
        wt.commit("base", allow_pointless=True, rev_id='A')
412
 
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
474
        wt.commit("base", allow_pointless=True, rev_id=b'A')
 
475
        self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
413
476
        try:
414
 
            from bzrlib.testament import Testament
415
477
            # monkey patch gpg signing mechanism
416
 
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
417
 
            config = MustSignConfig(branch)
418
 
            self.assertRaises(SigningFailed,
419
 
                              commit.Commit(config=config).commit,
 
478
            breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
 
479
            conf = config.MemoryStack(b'''
 
480
create_signatures=always
 
481
''')
 
482
            self.assertRaises(breezy.gpg.SigningFailed,
 
483
                              commit.Commit(config_stack=conf).commit,
420
484
                              message="base",
421
485
                              allow_pointless=True,
422
 
                              rev_id='B',
 
486
                              rev_id=b'B',
423
487
                              working_tree=wt)
424
488
            branch = Branch.open(self.get_url('.'))
425
 
            self.assertEqual(branch.revision_history(), ['A'])
426
 
            self.failIf(branch.repository.has_revision('B'))
 
489
            self.assertEqual(branch.last_revision(), b'A')
 
490
            self.assertFalse(branch.repository.has_revision(b'B'))
427
491
        finally:
428
 
            bzrlib.gpg.GPGStrategy = oldstrategy
 
492
            breezy.gpg.GPGStrategy = oldstrategy
429
493
 
430
494
    def test_commit_invokes_hooks(self):
431
 
        import bzrlib.commit as commit
 
495
        import breezy.commit as commit
432
496
        wt = self.make_branch_and_tree('.')
433
497
        branch = wt.branch
434
498
        calls = []
 
499
 
435
500
        def called(branch, rev_id):
436
501
            calls.append('called')
437
 
        bzrlib.ahook = called
 
502
        breezy.ahook = called
438
503
        try:
439
 
            config = BranchWithHooks(branch)
440
 
            commit.Commit(config=config).commit(
441
 
                            message = "base",
442
 
                            allow_pointless=True,
443
 
                            rev_id='A', working_tree = wt)
 
504
            conf = config.MemoryStack(b'post_commit=breezy.ahook breezy.ahook')
 
505
            commit.Commit(config_stack=conf).commit(
 
506
                message="base", allow_pointless=True, rev_id=b'A',
 
507
                working_tree=wt)
444
508
            self.assertEqual(['called', 'called'], calls)
445
509
        finally:
446
 
            del bzrlib.ahook
 
510
            del breezy.ahook
447
511
 
448
512
    def test_commit_object_doesnt_set_nick(self):
449
513
        # using the Commit object directly does not set the branch nick.
450
514
        wt = self.make_branch_and_tree('.')
451
515
        c = Commit()
452
516
        c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
453
 
        self.assertEquals(wt.branch.revno(), 1)
 
517
        self.assertEqual(wt.branch.revno(), 1)
454
518
        self.assertEqual({},
455
519
                         wt.branch.repository.get_revision(
456
 
                            wt.branch.last_revision()).properties)
 
520
            wt.branch.last_revision()).properties)
457
521
 
458
522
    def test_safe_master_lock(self):
459
523
        os.mkdir('master')
477
541
        bound_tree = self.make_branch_and_tree('bound')
478
542
        bound_tree.branch.bind(master_branch)
479
543
 
480
 
        self.build_tree_contents([('bound/content_file', 'initial contents\n')])
 
544
        self.build_tree_contents(
 
545
            [('bound/content_file', b'initial contents\n')])
481
546
        bound_tree.add(['content_file'])
482
547
        bound_tree.commit(message='woo!')
483
548
 
484
 
        other_bzrdir = master_branch.bzrdir.sprout('other')
 
549
        other_bzrdir = master_branch.controldir.sprout('other')
485
550
        other_tree = other_bzrdir.open_workingtree()
486
551
 
487
552
        # do a commit to the other branch changing the content file so
488
553
        # that our commit after merging will have a merged revision in the
489
554
        # content file history.
490
 
        self.build_tree_contents([('other/content_file', 'change in other\n')])
 
555
        self.build_tree_contents(
 
556
            [('other/content_file', b'change in other\n')])
491
557
        other_tree.commit('change in other')
492
558
 
493
559
        # do a merge into the bound branch from other, and then change the
494
560
        # content file locally to force a new revision (rather than using the
495
561
        # revision from other). This forces extra processing in commit.
496
562
        bound_tree.merge_from_branch(other_tree.branch)
497
 
        self.build_tree_contents([('bound/content_file', 'change in bound\n')])
 
563
        self.build_tree_contents(
 
564
            [('bound/content_file', b'change in bound\n')])
498
565
 
499
566
        # before #34959 was fixed, this failed with 'revision not present in
500
567
        # weave' when trying to implicitly push from the bound branch to the master
528
595
            'filetoleave']
529
596
            )
530
597
        this_tree.commit('create_files')
531
 
        other_dir = this_tree.bzrdir.sprout('other')
 
598
        other_dir = this_tree.controldir.sprout('other')
532
599
        other_tree = other_dir.open_workingtree()
533
600
        other_tree.lock_write()
534
601
        # perform the needed actions on the files and dirs.
536
603
            other_tree.rename_one('dirtorename', 'renameddir')
537
604
            other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
538
605
            other_tree.rename_one('filetorename', 'renamedfile')
539
 
            other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
 
606
            other_tree.rename_one(
 
607
                'filetoreparent', 'renameddir/reparentedfile')
540
608
            other_tree.remove(['dirtoremove', 'filetoremove'])
541
609
            self.build_tree_contents([
542
610
                ('other/newdir/', ),
543
 
                ('other/filetomodify', 'new content'),
544
 
                ('other/newfile', 'new file content')])
 
611
                ('other/filetomodify', b'new content'),
 
612
                ('other/newfile', b'new file content')])
545
613
            other_tree.add('newfile')
546
614
            other_tree.add('newdir/')
547
615
            other_tree.commit('modify all sample files and dirs.')
550
618
        this_tree.merge_from_branch(other_tree.branch)
551
619
        reporter = CapturingReporter()
552
620
        this_tree.commit('do the commit', reporter=reporter)
553
 
        expected = set([
 
621
        expected = {
554
622
            ('change', 'modified', 'filetomodify'),
555
623
            ('change', 'added', 'newdir'),
556
624
            ('change', 'added', 'newfile'),
560
628
            ('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
561
629
            ('deleted', 'dirtoremove'),
562
630
            ('deleted', 'filetoremove'),
563
 
            ])
 
631
            }
564
632
        result = set(reporter.calls)
565
633
        missing = expected - result
566
634
        new = result - expected
575
643
        tree.remove(['a', 'b'])
576
644
        tree.commit('removed a', specific_files='a')
577
645
        basis = tree.basis_tree()
578
 
        tree.lock_read()
579
 
        try:
580
 
            self.assertIs(None, basis.path2id('a'))
581
 
            self.assertFalse(basis.path2id('b') is None)
582
 
        finally:
583
 
            tree.unlock()
 
646
        with tree.lock_read():
 
647
            self.assertFalse(basis.is_versioned('a'))
 
648
            self.assertTrue(basis.is_versioned('b'))
584
649
 
585
650
    def test_commit_saves_1ms_timestamp(self):
586
651
        """Passing in a timestamp is saved with 1ms resolution"""
588
653
        self.build_tree(['a'])
589
654
        tree.add('a')
590
655
        tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
591
 
                    rev_id='a1')
 
656
                    rev_id=b'a1')
592
657
 
593
 
        rev = tree.branch.repository.get_revision('a1')
 
658
        rev = tree.branch.repository.get_revision(b'a1')
594
659
        self.assertEqual(1153248633.419, rev.timestamp)
595
660
 
596
661
    def test_commit_has_1ms_resolution(self):
598
663
        tree = self.make_branch_and_tree('.')
599
664
        self.build_tree(['a'])
600
665
        tree.add('a')
601
 
        tree.commit('added a', rev_id='a1')
 
666
        tree.commit('added a', rev_id=b'a1')
602
667
 
603
 
        rev = tree.branch.repository.get_revision('a1')
 
668
        rev = tree.branch.repository.get_revision(b'a1')
604
669
        timestamp = rev.timestamp
605
670
        timestamp_1ms = round(timestamp, 3)
606
671
        self.assertEqual(timestamp_1ms, timestamp)
607
672
 
608
 
    def assertBasisTreeKind(self, kind, tree, file_id):
 
673
    def assertBasisTreeKind(self, kind, tree, path):
609
674
        basis = tree.basis_tree()
610
675
        basis.lock_read()
611
676
        try:
612
 
            self.assertEqual(kind, basis.kind(file_id))
 
677
            self.assertEqual(kind, basis.kind(path))
613
678
        finally:
614
679
            basis.unlock()
615
680
 
 
681
    def test_unsupported_symlink_commit(self):
 
682
        self.requireFeature(SymlinkFeature)
 
683
        tree = self.make_branch_and_tree('.')
 
684
        self.build_tree(['hello'])
 
685
        tree.add('hello')
 
686
        tree.commit('added hello', rev_id=b'hello_id')
 
687
        os.symlink('hello', 'foo')
 
688
        tree.add('foo')
 
689
        tree.commit('added foo', rev_id=b'foo_id')
 
690
        log = BytesIO()
 
691
        trace.push_log_file(log)
 
692
        os_symlink = getattr(os, 'symlink', None)
 
693
        os.symlink = None
 
694
        try:
 
695
            # At this point as bzr thinks symlinks are not supported
 
696
            # we should get a warning about symlink foo and bzr should
 
697
            # not think its removed.
 
698
            os.unlink('foo')
 
699
            self.build_tree(['world'])
 
700
            tree.add('world')
 
701
            tree.commit('added world', rev_id=b'world_id')
 
702
        finally:
 
703
            if os_symlink:
 
704
                os.symlink = os_symlink
 
705
        self.assertContainsRe(
 
706
            log.getvalue(),
 
707
            b'Ignoring "foo" as symlinks are not '
 
708
            b'supported on this filesystem\\.')
 
709
 
616
710
    def test_commit_kind_changes(self):
617
711
        self.requireFeature(SymlinkFeature)
618
712
        tree = self.make_branch_and_tree('.')
619
713
        os.symlink('target', 'name')
620
 
        tree.add('name', 'a-file-id')
 
714
        tree.add('name', b'a-file-id')
621
715
        tree.commit('Added a symlink')
622
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
716
        self.assertBasisTreeKind('symlink', tree, 'name')
623
717
 
624
718
        os.unlink('name')
625
719
        self.build_tree(['name'])
626
720
        tree.commit('Changed symlink to file')
627
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
721
        self.assertBasisTreeKind('file', tree, 'name')
628
722
 
629
723
        os.unlink('name')
630
724
        os.symlink('target', 'name')
631
725
        tree.commit('file to symlink')
632
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
726
        self.assertBasisTreeKind('symlink', tree, 'name')
633
727
 
634
728
        os.unlink('name')
635
729
        os.mkdir('name')
636
730
        tree.commit('symlink to directory')
637
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
731
        self.assertBasisTreeKind('directory', tree, 'name')
638
732
 
639
733
        os.rmdir('name')
640
734
        os.symlink('target', 'name')
641
735
        tree.commit('directory to symlink')
642
 
        self.assertBasisTreeKind('symlink', tree, 'a-file-id')
 
736
        self.assertBasisTreeKind('symlink', tree, 'name')
643
737
 
644
738
        # prepare for directory <-> file tests
645
739
        os.unlink('name')
646
740
        os.mkdir('name')
647
741
        tree.commit('symlink to directory')
648
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
742
        self.assertBasisTreeKind('directory', tree, 'name')
649
743
 
650
744
        os.rmdir('name')
651
745
        self.build_tree(['name'])
652
746
        tree.commit('Changed directory to file')
653
 
        self.assertBasisTreeKind('file', tree, 'a-file-id')
 
747
        self.assertBasisTreeKind('file', tree, 'name')
654
748
 
655
749
        os.unlink('name')
656
750
        os.mkdir('name')
657
751
        tree.commit('file to directory')
658
 
        self.assertBasisTreeKind('directory', tree, 'a-file-id')
 
752
        self.assertBasisTreeKind('directory', tree, 'name')
659
753
 
660
754
    def test_commit_unversioned_specified(self):
661
755
        """Commit should raise if specified files isn't in basis or worktree"""
681
775
        tree = self.make_branch_and_tree('.')
682
776
        try:
683
777
            tree.commit()
684
 
        except Exception, e:
 
778
        except Exception as e:
685
779
            self.assertTrue(isinstance(e, BzrError))
686
780
            self.assertEqual('The message or message_callback keyword'
687
781
                             ' parameter is required for commit().', str(e))
708
802
        cb = self.Callback(u'commit 2', self)
709
803
        repository = tree.branch.repository
710
804
        # simulate network failure
 
805
 
711
806
        def raise_(self, arg, arg2, arg3=None, arg4=None):
712
807
            raise errors.NoSuchFile('foo')
713
808
        repository.add_inventory = raise_
720
815
        tree = self.make_branch_and_tree('foo')
721
816
        # pending merge would turn into a left parent
722
817
        tree.commit('commit 1')
723
 
        tree.add_parent_tree_id('example')
 
818
        tree.add_parent_tree_id(b'example')
724
819
        self.build_tree(['foo/bar', 'foo/baz'])
725
820
        tree.add(['bar', 'baz'])
726
 
        err = self.assertRaises(errors.CannotCommitSelectedFileMerge,
727
 
            tree.commit, 'commit 2', specific_files=['bar', 'baz'])
 
821
        err = self.assertRaises(CannotCommitSelectedFileMerge,
 
822
                                tree.commit, 'commit 2', specific_files=['bar', 'baz'])
728
823
        self.assertEqual(['bar', 'baz'], err.files)
729
824
        self.assertEqual('Selected-file commit of merges is not supported'
730
825
                         ' yet: files bar, baz', str(err))
751
846
        self.assertFalse('authors' in rev.properties)
752
847
 
753
848
    def test_commit_author(self):
754
 
        """Passing a non-empty author kwarg to MutableTree.commit should add
 
849
        """Passing a non-empty authors kwarg to MutableTree.commit should add
755
850
        the 'author' revision property.
756
851
        """
757
852
        tree = self.make_branch_and_tree('foo')
758
 
        rev_id = self.callDeprecated(['The parameter author was '
759
 
                'deprecated in version 1.13. Use authors instead'],
760
 
                tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
 
853
        rev_id = tree.commit(
 
854
            'commit 1',
 
855
            authors=['John Doe <jdoe@example.com>'])
761
856
        rev = tree.branch.repository.get_revision(rev_id)
762
857
        self.assertEqual('John Doe <jdoe@example.com>',
763
858
                         rev.properties['authors'])
774
869
    def test_multiple_authors(self):
775
870
        tree = self.make_branch_and_tree('foo')
776
871
        rev_id = tree.commit('commit 1',
777
 
                authors=['John Doe <jdoe@example.com>',
778
 
                         'Jane Rey <jrey@example.com>'])
 
872
                             authors=['John Doe <jdoe@example.com>',
 
873
                                      'Jane Rey <jrey@example.com>'])
779
874
        rev = tree.branch.repository.get_revision(rev_id)
780
875
        self.assertEqual('John Doe <jdoe@example.com>\n'
781
 
                'Jane Rey <jrey@example.com>', rev.properties['authors'])
 
876
                         'Jane Rey <jrey@example.com>', rev.properties['authors'])
782
877
        self.assertFalse('author' in rev.properties)
783
878
 
784
 
    def test_author_and_authors_incompatible(self):
785
 
        tree = self.make_branch_and_tree('foo')
786
 
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
787
 
                authors=['John Doe <jdoe@example.com>',
788
 
                         'Jane Rey <jrey@example.com>'],
789
 
                author="Jack Me <jme@example.com>")
790
 
 
791
879
    def test_author_with_newline_rejected(self):
792
880
        tree = self.make_branch_and_tree('foo')
793
881
        self.assertRaises(AssertionError, tree.commit, 'commit 1',
794
 
                authors=['John\nDoe <jdoe@example.com>'])
 
882
                          authors=['John\nDoe <jdoe@example.com>'])
795
883
 
796
884
    def test_commit_with_checkout_and_branch_sharing_repo(self):
797
885
        repo = self.make_repository('repo', shared=True)
798
886
        # make_branch_and_tree ignores shared repos
799
 
        branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
 
887
        branch = controldir.ControlDir.create_branch_convenience('repo/branch')
800
888
        tree2 = branch.create_checkout('repo/tree2')
801
 
        tree2.commit('message', rev_id='rev1')
802
 
        self.assertTrue(tree2.branch.repository.has_revision('rev1'))
 
889
        tree2.commit('message', rev_id=b'rev1')
 
890
        self.assertTrue(tree2.branch.repository.has_revision(b'rev1'))
 
891
 
 
892
 
 
893
class FilterExcludedTests(TestCase):
 
894
 
 
895
    def test_add_file_not_excluded(self):
 
896
        changes = [
 
897
            ('fid', (None, 'newpath'),
 
898
             0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
 
899
             ('file', 'file'), (True, True))]
 
900
        self.assertEqual(changes, list(
 
901
            filter_excluded(changes, ['otherpath'])))
 
902
 
 
903
    def test_add_file_excluded(self):
 
904
        changes = [
 
905
            ('fid', (None, 'newpath'),
 
906
             0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
 
907
             ('file', 'file'), (True, True))]
 
908
        self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
 
909
 
 
910
    def test_delete_file_excluded(self):
 
911
        changes = [
 
912
            ('fid', ('somepath', None),
 
913
             0, (False, None), ('pid', None), ('newpath', None),
 
914
             ('file', None), (True, None))]
 
915
        self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
 
916
 
 
917
    def test_move_from_or_to_excluded(self):
 
918
        changes = [
 
919
            ('fid', ('oldpath', 'newpath'),
 
920
             0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
 
921
             ('file', 'file'), (True, True))]
 
922
        self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
 
923
        self.assertEqual([], list(filter_excluded(changes, ['newpath'])))