/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: 2019-02-03 01:42:11 UTC
  • mto: This revision was merged to the branch mainline in revision 7267.
  • Revision ID: jelmer@jelmer.uk-20190203014211-poj1fv922sejfsb4
Don't require that short git shas have an even number of characters.

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