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

[merge] robertc's integration, updated tests to check for retcode=3

Show diffs side-by-side

added added

removed removed

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