/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

  • Committer: Jelmer Vernooij
  • Date: 2020-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

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