/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-06-15 21:45:04 UTC
  • mto: This revision was merged to the branch mainline in revision 7337.
  • Revision ID: jelmer@jelmer.uk-20190615214504-bn2rp571fj5pitie
Fix tests on Python3.

Show diffs side-by-side

added added

removed removed

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