28
from bzrlib.branch import Branch
29
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
30
from bzrlib.commit import Commit, NullCommitReporter
31
from bzrlib.config import BranchConfig
32
from bzrlib.errors import (PointlessCommit, BzrError, SigningFailed,
34
from bzrlib.tests import SymlinkFeature, TestCaseWithTransport
35
from bzrlib.workingtree import WorkingTree
26
from ..branch import Branch
27
from ..bzr.bzrdir import BzrDirMetaFormat1
28
from ..commit import (
33
from ..errors import (
40
TestCaseWithTransport,
43
from .features import (
46
from .matchers import MatchesAncestry
38
49
# TODO: Test commit with some added, and added-but-missing files
40
class MustSignConfig(BranchConfig):
42
def signature_needed(self):
45
def gpg_signing_command(self):
49
class BranchWithHooks(BranchConfig):
51
def post_commit(self):
52
return "bzrlib.ahook bzrlib.ahook"
51
class MustSignConfig(config.MemoryStack):
54
super(MustSignConfig, self).__init__('''
55
create_signatures=always
55
59
class CapturingReporter(NullCommitReporter):
81
85
"""Commit and check two versions of a single file."""
82
86
wt = self.make_branch_and_tree('.')
84
file('hello', 'w').write('hello world')
88
with file('hello', 'w') as f: f.write('hello world')
86
wt.commit(message='add hello')
90
rev1 = wt.commit(message='add hello')
87
91
file_id = wt.path2id('hello')
89
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
93
with file('hello', 'w') as f: f.write('version 2')
94
rev2 = wt.commit(message='commit 2')
92
eq = self.assertEquals
94
rh = b.revision_history()
95
rev = b.repository.get_revision(rh[0])
98
rev = b.repository.get_revision(rev1)
96
99
eq(rev.message, 'add hello')
98
tree1 = b.repository.revision_tree(rh[0])
101
tree1 = b.repository.revision_tree(rev1)
100
103
text = tree1.get_file_text(file_id)
102
105
self.assertEqual('hello world', text)
104
tree2 = b.repository.revision_tree(rh[1])
107
tree2 = b.repository.revision_tree(rev2)
105
108
tree2.lock_read()
106
109
text = tree2.get_file_text(file_id)
108
111
self.assertEqual('version 2', text)
113
def test_commit_lossy_native(self):
114
"""Attempt a lossy commit to a native branch."""
115
wt = self.make_branch_and_tree('.')
117
with file('hello', 'w') as f: f.write('hello world')
119
revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
120
self.assertEqual('revid', revid)
122
def test_commit_lossy_foreign(self):
123
"""Attempt a lossy commit to a foreign branch."""
124
test_foreign.register_dummy_foreign_for_test(self)
125
wt = self.make_branch_and_tree('.',
126
format=test_foreign.DummyForeignVcsDirFormat())
128
with file('hello', 'w') as f: f.write('hello world')
130
revid = wt.commit(message='add hello', lossy=True,
131
timestamp=1302659388, timezone=0)
132
self.assertEqual('dummy-v1:1302659388.0-0-UNKNOWN', revid)
134
def test_commit_bound_lossy_foreign(self):
135
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
136
test_foreign.register_dummy_foreign_for_test(self)
137
foreign_branch = self.make_branch('foreign',
138
format=test_foreign.DummyForeignVcsDirFormat())
139
wt = foreign_branch.create_checkout("local")
141
with file('local/hello', 'w') as f: f.write('hello world')
143
revid = wt.commit(message='add hello', lossy=True,
144
timestamp=1302659388, timezone=0)
145
self.assertEqual('dummy-v1:1302659388.0-0-0', revid)
146
self.assertEqual('dummy-v1:1302659388.0-0-0',
147
foreign_branch.last_revision())
148
self.assertEqual('dummy-v1:1302659388.0-0-0',
149
wt.branch.last_revision())
110
151
def test_missing_commit(self):
111
152
"""Test a commit with a missing file"""
112
153
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
155
with file('hello', 'w') as f: f.write('hello world')
115
156
wt.add(['hello'], ['hello-id'])
116
157
wt.commit(message='add hello')
118
159
os.remove('hello')
119
wt.commit('removed hello', rev_id='rev2')
160
reporter = CapturingReporter()
161
wt.commit('removed hello', rev_id='rev2', reporter=reporter)
163
[('missing', u'hello'), ('deleted', u'hello')],
121
166
tree = b.repository.revision_tree('rev2')
122
167
self.assertFalse(tree.has_id('hello-id'))
145
190
"""Commit refuses unless there are changes or it's forced."""
146
191
wt = self.make_branch_and_tree('.')
148
file('hello', 'w').write('hello')
193
with file('hello', 'w') as f: f.write('hello')
149
194
wt.add(['hello'])
150
195
wt.commit(message='add hello')
151
self.assertEquals(b.revno(), 1)
196
self.assertEqual(b.revno(), 1)
152
197
self.assertRaises(PointlessCommit,
155
200
allow_pointless=False)
156
self.assertEquals(b.revno(), 1)
201
self.assertEqual(b.revno(), 1)
158
203
def test_commit_empty(self):
159
204
"""Commiting an empty tree works."""
165
210
message='empty tree',
166
211
allow_pointless=False)
167
212
wt.commit(message='empty tree', allow_pointless=True)
168
self.assertEquals(b.revno(), 2)
213
self.assertEqual(b.revno(), 2)
170
215
def test_selective_delete(self):
171
216
"""Selective commit in tree with deletions"""
172
217
wt = self.make_branch_and_tree('.')
174
file('hello', 'w').write('hello')
175
file('buongia', 'w').write('buongia')
219
with file('hello', 'w') as f: f.write('hello')
220
with file('buongia', 'w') as f: f.write('buongia')
176
221
wt.add(['hello', 'buongia'],
177
222
['hello-id', 'buongia-id'])
178
223
wt.commit(message='add files',
179
224
rev_id='test@rev-1')
181
226
os.remove('hello')
182
file('buongia', 'w').write('new text')
227
with file('buongia', 'w') as f: f.write('new text')
183
228
wt.commit(message='update text',
184
229
specific_files=['buongia'],
185
230
allow_pointless=False,
190
235
allow_pointless=False,
191
236
rev_id='test@rev-3')
193
eq = self.assertEquals
238
eq = self.assertEqual
196
241
tree2 = b.repository.revision_tree('test@rev-2')
197
242
tree2.lock_read()
198
243
self.addCleanup(tree2.unlock)
199
244
self.assertTrue(tree2.has_filename('hello'))
200
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
201
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
245
self.assertEqual(tree2.get_file_text('hello-id'), 'hello')
246
self.assertEqual(tree2.get_file_text('buongia-id'), 'new text')
203
248
tree3 = b.repository.revision_tree('test@rev-3')
204
249
tree3.lock_read()
205
250
self.addCleanup(tree3.unlock)
206
251
self.assertFalse(tree3.has_filename('hello'))
207
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
252
self.assertEqual(tree3.get_file_text('buongia-id'), 'new text')
209
254
def test_commit_rename(self):
210
255
"""Test commit of a revision where a file is renamed."""
217
262
tree.rename_one('hello', 'fruity')
218
263
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
220
eq = self.assertEquals
265
eq = self.assertEqual
221
266
tree1 = b.repository.revision_tree('test@rev-1')
222
267
tree1.lock_read()
223
268
self.addCleanup(tree1.unlock)
224
269
eq(tree1.id2path('hello-id'), 'hello')
225
270
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
226
271
self.assertFalse(tree1.has_filename('fruity'))
227
self.check_inventory_shape(tree1.inventory, ['hello'])
228
ie = tree1.inventory['hello-id']
229
eq(ie.revision, 'test@rev-1')
272
self.check_tree_shape(tree1, ['hello'])
273
eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
231
275
tree2 = b.repository.revision_tree('test@rev-2')
232
276
tree2.lock_read()
233
277
self.addCleanup(tree2.unlock)
234
278
eq(tree2.id2path('hello-id'), 'fruity')
235
279
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
236
self.check_inventory_shape(tree2.inventory, ['fruity'])
237
ie = tree2.inventory['hello-id']
238
eq(ie.revision, 'test@rev-2')
280
self.check_tree_shape(tree2, ['fruity'])
281
eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
240
283
def test_reused_rev_id(self):
241
284
"""Test that a revision id cannot be reused in a branch"""
315
356
for i in range(4):
316
file('hello', 'w').write((str(i) * 4) + '\n')
357
with file('hello', 'w') as f: f.write((str(i) * 4) + '\n')
318
359
wt.add(['hello'], ['hello-id'])
319
360
rev_id = 'test@rev-%d' % (i+1)
320
361
rev_ids.append(rev_id)
321
362
wt.commit(message='rev %d' % (i+1),
323
eq = self.assertEquals
324
eq(b.revision_history(), rev_ids)
325
364
for i in range(4):
326
anc = b.repository.get_ancestry(rev_ids[i])
327
eq(anc, [None] + rev_ids[:i+1])
365
self.assertThat(rev_ids[:i+1],
366
MatchesAncestry(b.repository, rev_ids[i]))
329
368
def test_commit_new_subdir_child_selective(self):
330
369
wt = self.make_branch_and_tree('.')
342
381
def test_strict_commit(self):
343
382
"""Try and commit with unknown files and strict = True, should fail."""
344
from bzrlib.errors import StrictCommitFailed
383
from ..errors import StrictCommitFailed
345
384
wt = self.make_branch_and_tree('.')
347
file('hello', 'w').write('hello world')
386
with file('hello', 'w') as f: f.write('hello world')
349
file('goodbye', 'w').write('goodbye cruel world!')
388
with file('goodbye', 'w') as f: f.write('goodbye cruel world!')
350
389
self.assertRaises(StrictCommitFailed, wt.commit,
351
390
message='add hello but not goodbye', strict=True)
353
392
def test_strict_commit_without_unknowns(self):
354
393
"""Try and commit with no unknown files and strict = True,
356
from bzrlib.errors import StrictCommitFailed
357
395
wt = self.make_branch_and_tree('.')
359
file('hello', 'w').write('hello world')
397
with file('hello', 'w') as f: f.write('hello world')
361
399
wt.commit(message='add hello', strict=True)
375
413
wt = self.make_branch_and_tree('.')
377
file('hello', 'w').write('hello world')
415
with file('hello', 'w') as f: f.write('hello world')
379
417
wt.commit(message='add hello', strict=False)
381
419
def test_signed_commit(self):
383
import bzrlib.commit as commit
384
oldstrategy = bzrlib.gpg.GPGStrategy
421
import breezy.commit as commit
422
oldstrategy = breezy.gpg.GPGStrategy
385
423
wt = self.make_branch_and_tree('.')
386
424
branch = wt.branch
387
425
wt.commit("base", allow_pointless=True, rev_id='A')
388
self.failIf(branch.repository.has_signature_for_revision_id('A'))
426
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
390
from bzrlib.testament import Testament
428
from ..testament import Testament
391
429
# monkey patch gpg signing mechanism
392
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
393
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
394
allow_pointless=True,
430
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
431
conf = config.MemoryStack('''
432
create_signatures=always
434
commit.Commit(config_stack=conf).commit(
435
message="base", allow_pointless=True, rev_id='B',
398
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
438
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
399
439
self.assertEqual(sign(Testament.from_revision(branch.repository,
400
'B').as_short_text()),
440
'B').as_short_text()),
401
441
branch.repository.get_signature_text('B'))
403
bzrlib.gpg.GPGStrategy = oldstrategy
443
breezy.gpg.GPGStrategy = oldstrategy
405
445
def test_commit_failed_signature(self):
407
import bzrlib.commit as commit
408
oldstrategy = bzrlib.gpg.GPGStrategy
447
import breezy.commit as commit
448
oldstrategy = breezy.gpg.GPGStrategy
409
449
wt = self.make_branch_and_tree('.')
410
450
branch = wt.branch
411
451
wt.commit("base", allow_pointless=True, rev_id='A')
412
self.failIf(branch.repository.has_signature_for_revision_id('A'))
452
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
414
from bzrlib.testament import Testament
415
454
# monkey patch gpg signing mechanism
416
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
417
config = MustSignConfig(branch)
418
self.assertRaises(SigningFailed,
419
commit.Commit(config=config).commit,
455
breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
456
conf = config.MemoryStack('''
457
create_signatures=always
459
self.assertRaises(breezy.gpg.SigningFailed,
460
commit.Commit(config_stack=conf).commit,
421
462
allow_pointless=True,
424
465
branch = Branch.open(self.get_url('.'))
425
self.assertEqual(branch.revision_history(), ['A'])
426
self.failIf(branch.repository.has_revision('B'))
466
self.assertEqual(branch.last_revision(), 'A')
467
self.assertFalse(branch.repository.has_revision('B'))
428
bzrlib.gpg.GPGStrategy = oldstrategy
469
breezy.gpg.GPGStrategy = oldstrategy
430
471
def test_commit_invokes_hooks(self):
431
import bzrlib.commit as commit
472
import breezy.commit as commit
432
473
wt = self.make_branch_and_tree('.')
433
474
branch = wt.branch
435
476
def called(branch, rev_id):
436
477
calls.append('called')
437
bzrlib.ahook = called
478
breezy.ahook = called
439
config = BranchWithHooks(branch)
440
commit.Commit(config=config).commit(
442
allow_pointless=True,
443
rev_id='A', working_tree = wt)
480
conf = config.MemoryStack('post_commit=breezy.ahook breezy.ahook')
481
commit.Commit(config_stack=conf).commit(
482
message = "base", allow_pointless=True, rev_id='A',
444
484
self.assertEqual(['called', 'called'], calls)
448
488
def test_commit_object_doesnt_set_nick(self):
449
489
# using the Commit object directly does not set the branch nick.
450
490
wt = self.make_branch_and_tree('.')
452
492
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
453
self.assertEquals(wt.branch.revno(), 1)
493
self.assertEqual(wt.branch.revno(), 1)
454
494
self.assertEqual({},
455
495
wt.branch.repository.get_revision(
456
496
wt.branch.last_revision()).properties)
751
791
self.assertFalse('authors' in rev.properties)
753
793
def test_commit_author(self):
754
"""Passing a non-empty author kwarg to MutableTree.commit should add
794
"""Passing a non-empty authors kwarg to MutableTree.commit should add
755
795
the 'author' revision property.
757
797
tree = self.make_branch_and_tree('foo')
758
rev_id = self.callDeprecated(['The parameter author was '
759
'deprecated in version 1.13. Use authors instead'],
760
tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
798
rev_id = tree.commit(
800
authors=['John Doe <jdoe@example.com>'])
761
801
rev = tree.branch.repository.get_revision(rev_id)
762
802
self.assertEqual('John Doe <jdoe@example.com>',
763
803
rev.properties['authors'])
781
821
'Jane Rey <jrey@example.com>', rev.properties['authors'])
782
822
self.assertFalse('author' in rev.properties)
784
def test_author_and_authors_incompatible(self):
785
tree = self.make_branch_and_tree('foo')
786
self.assertRaises(AssertionError, tree.commit, 'commit 1',
787
authors=['John Doe <jdoe@example.com>',
788
'Jane Rey <jrey@example.com>'],
789
author="Jack Me <jme@example.com>")
791
824
def test_author_with_newline_rejected(self):
792
825
tree = self.make_branch_and_tree('foo')
793
826
self.assertRaises(AssertionError, tree.commit, 'commit 1',
796
829
def test_commit_with_checkout_and_branch_sharing_repo(self):
797
830
repo = self.make_repository('repo', shared=True)
798
831
# make_branch_and_tree ignores shared repos
799
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
832
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
800
833
tree2 = branch.create_checkout('repo/tree2')
801
834
tree2.commit('message', rev_id='rev1')
802
835
self.assertTrue(tree2.branch.repository.has_revision('rev1'))
838
class FilterExcludedTests(TestCase):
840
def test_add_file_not_excluded(self):
842
('fid', (None, 'newpath'),
843
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
844
('file', 'file'), (True, True))]
845
self.assertEqual(changes, list(filter_excluded(changes, ['otherpath'])))
847
def test_add_file_excluded(self):
849
('fid', (None, 'newpath'),
850
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
851
('file', 'file'), (True, True))]
852
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
854
def test_delete_file_excluded(self):
856
('fid', ('somepath', None),
857
0, (False, None), ('pid', None), ('newpath', None),
858
('file', None), (True, None))]
859
self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
861
def test_move_from_or_to_excluded(self):
863
('fid', ('oldpath', 'newpath'),
864
0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
865
('file', 'file'), (True, True))]
866
self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
867
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))