/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 bzrlib/tests/test_commit.py

  • Committer: Andrew Bennetts
  • Date: 2011-06-09 07:38:32 UTC
  • mto: This revision was merged to the branch mainline in revision 5964.
  • Revision ID: andrew.bennetts@canonical.com-20110609073832-dt6oww033iexli4l
Fix thinko in wording regarding stacking invariants and revisions with multiple parents.

Show diffs side-by-side

added added

removed removed

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