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 (
29
CannotCommitSelectedFileMerge,
35
from ..errors import (
41
TestCaseWithTransport,
44
from .features import (
47
from .matchers import MatchesAncestry
38
50
# 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"
52
class MustSignConfig(config.MemoryStack):
55
super(MustSignConfig, self).__init__('''
56
create_signatures=always
55
60
class CapturingReporter(NullCommitReporter):
81
86
"""Commit and check two versions of a single file."""
82
87
wt = self.make_branch_and_tree('.')
84
file('hello', 'w').write('hello world')
89
with open('hello', 'w') as f: f.write('hello world')
86
wt.commit(message='add hello')
91
rev1 = wt.commit(message='add hello')
87
92
file_id = wt.path2id('hello')
89
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
94
with open('hello', 'w') as f: f.write('version 2')
95
rev2 = wt.commit(message='commit 2')
92
eq = self.assertEquals
94
rh = b.revision_history()
95
rev = b.repository.get_revision(rh[0])
99
rev = b.repository.get_revision(rev1)
96
100
eq(rev.message, 'add hello')
98
tree1 = b.repository.revision_tree(rh[0])
102
tree1 = b.repository.revision_tree(rev1)
100
text = tree1.get_file_text(file_id)
104
text = tree1.get_file_text('hello')
102
106
self.assertEqual('hello world', text)
104
tree2 = b.repository.revision_tree(rh[1])
108
tree2 = b.repository.revision_tree(rev2)
105
109
tree2.lock_read()
106
text = tree2.get_file_text(file_id)
110
text = tree2.get_file_text('hello')
108
112
self.assertEqual('version 2', text)
114
def test_commit_lossy_native(self):
115
"""Attempt a lossy commit to a native branch."""
116
wt = self.make_branch_and_tree('.')
118
with open('hello', 'w') as f: f.write('hello world')
120
revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
121
self.assertEqual('revid', revid)
123
def test_commit_lossy_foreign(self):
124
"""Attempt a lossy commit to a foreign branch."""
125
test_foreign.register_dummy_foreign_for_test(self)
126
wt = self.make_branch_and_tree('.',
127
format=test_foreign.DummyForeignVcsDirFormat())
129
with open('hello', 'w') as f: f.write('hello world')
131
revid = wt.commit(message='add hello', lossy=True,
132
timestamp=1302659388, timezone=0)
133
self.assertEqual('dummy-v1:1302659388.0-0-UNKNOWN', revid)
135
def test_commit_bound_lossy_foreign(self):
136
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
137
test_foreign.register_dummy_foreign_for_test(self)
138
foreign_branch = self.make_branch('foreign',
139
format=test_foreign.DummyForeignVcsDirFormat())
140
wt = foreign_branch.create_checkout("local")
142
with open('local/hello', 'w') as f: f.write('hello world')
144
revid = wt.commit(message='add hello', lossy=True,
145
timestamp=1302659388, timezone=0)
146
self.assertEqual('dummy-v1:1302659388.0-0-0', revid)
147
self.assertEqual('dummy-v1:1302659388.0-0-0',
148
foreign_branch.last_revision())
149
self.assertEqual('dummy-v1:1302659388.0-0-0',
150
wt.branch.last_revision())
110
152
def test_missing_commit(self):
111
153
"""Test a commit with a missing file"""
112
154
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
156
with open('hello', 'w') as f: f.write('hello world')
115
157
wt.add(['hello'], ['hello-id'])
116
158
wt.commit(message='add hello')
118
160
os.remove('hello')
119
wt.commit('removed hello', rev_id='rev2')
161
reporter = CapturingReporter()
162
wt.commit('removed hello', rev_id='rev2', reporter=reporter)
164
[('missing', u'hello'), ('deleted', u'hello')],
121
167
tree = b.repository.revision_tree('rev2')
122
168
self.assertFalse(tree.has_id('hello-id'))
145
191
"""Commit refuses unless there are changes or it's forced."""
146
192
wt = self.make_branch_and_tree('.')
148
file('hello', 'w').write('hello')
194
with open('hello', 'w') as f: f.write('hello')
149
195
wt.add(['hello'])
150
196
wt.commit(message='add hello')
151
self.assertEquals(b.revno(), 1)
197
self.assertEqual(b.revno(), 1)
152
198
self.assertRaises(PointlessCommit,
155
201
allow_pointless=False)
156
self.assertEquals(b.revno(), 1)
202
self.assertEqual(b.revno(), 1)
158
204
def test_commit_empty(self):
159
205
"""Commiting an empty tree works."""
165
211
message='empty tree',
166
212
allow_pointless=False)
167
213
wt.commit(message='empty tree', allow_pointless=True)
168
self.assertEquals(b.revno(), 2)
214
self.assertEqual(b.revno(), 2)
170
216
def test_selective_delete(self):
171
217
"""Selective commit in tree with deletions"""
172
218
wt = self.make_branch_and_tree('.')
174
file('hello', 'w').write('hello')
175
file('buongia', 'w').write('buongia')
220
with open('hello', 'w') as f: f.write('hello')
221
with open('buongia', 'w') as f: f.write('buongia')
176
222
wt.add(['hello', 'buongia'],
177
223
['hello-id', 'buongia-id'])
178
224
wt.commit(message='add files',
179
225
rev_id='test@rev-1')
181
227
os.remove('hello')
182
file('buongia', 'w').write('new text')
228
with open('buongia', 'w') as f: f.write('new text')
183
229
wt.commit(message='update text',
184
230
specific_files=['buongia'],
185
231
allow_pointless=False,
190
236
allow_pointless=False,
191
237
rev_id='test@rev-3')
193
eq = self.assertEquals
239
eq = self.assertEqual
196
242
tree2 = b.repository.revision_tree('test@rev-2')
197
243
tree2.lock_read()
198
244
self.addCleanup(tree2.unlock)
199
245
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')
246
self.assertEqual(tree2.get_file_text('hello'), 'hello')
247
self.assertEqual(tree2.get_file_text('buongia'), 'new text')
203
249
tree3 = b.repository.revision_tree('test@rev-3')
204
250
tree3.lock_read()
205
251
self.addCleanup(tree3.unlock)
206
252
self.assertFalse(tree3.has_filename('hello'))
207
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
253
self.assertEqual(tree3.get_file_text('buongia'), 'new text')
209
255
def test_commit_rename(self):
210
256
"""Test commit of a revision where a file is renamed."""
217
263
tree.rename_one('hello', 'fruity')
218
264
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
220
eq = self.assertEquals
266
eq = self.assertEqual
221
267
tree1 = b.repository.revision_tree('test@rev-1')
222
268
tree1.lock_read()
223
269
self.addCleanup(tree1.unlock)
224
270
eq(tree1.id2path('hello-id'), 'hello')
225
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
271
eq(tree1.get_file_text('hello'), 'contents of hello\n')
226
272
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')
273
self.check_tree_shape(tree1, ['hello'])
274
eq(tree1.get_file_revision('hello'), 'test@rev-1')
231
276
tree2 = b.repository.revision_tree('test@rev-2')
232
277
tree2.lock_read()
233
278
self.addCleanup(tree2.unlock)
234
279
eq(tree2.id2path('hello-id'), 'fruity')
235
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
236
self.check_inventory_shape(tree2.inventory, ['fruity'])
237
ie = tree2.inventory['hello-id']
238
eq(ie.revision, 'test@rev-2')
280
eq(tree2.get_file_text('fruity'), 'contents of hello\n')
281
self.check_tree_shape(tree2, ['fruity'])
282
eq(tree2.get_file_revision('fruity'), 'test@rev-2')
240
284
def test_reused_rev_id(self):
241
285
"""Test that a revision id cannot be reused in a branch"""
315
357
for i in range(4):
316
file('hello', 'w').write((str(i) * 4) + '\n')
358
with open('hello', 'w') as f: f.write((str(i) * 4) + '\n')
318
360
wt.add(['hello'], ['hello-id'])
319
361
rev_id = 'test@rev-%d' % (i+1)
320
362
rev_ids.append(rev_id)
321
363
wt.commit(message='rev %d' % (i+1),
323
eq = self.assertEquals
324
eq(b.revision_history(), rev_ids)
325
365
for i in range(4):
326
anc = b.repository.get_ancestry(rev_ids[i])
327
eq(anc, [None] + rev_ids[:i+1])
366
self.assertThat(rev_ids[:i+1],
367
MatchesAncestry(b.repository, rev_ids[i]))
329
369
def test_commit_new_subdir_child_selective(self):
330
370
wt = self.make_branch_and_tree('.')
342
382
def test_strict_commit(self):
343
383
"""Try and commit with unknown files and strict = True, should fail."""
344
from bzrlib.errors import StrictCommitFailed
384
from ..errors import StrictCommitFailed
345
385
wt = self.make_branch_and_tree('.')
347
file('hello', 'w').write('hello world')
387
with open('hello', 'w') as f: f.write('hello world')
349
file('goodbye', 'w').write('goodbye cruel world!')
389
with open('goodbye', 'w') as f: f.write('goodbye cruel world!')
350
390
self.assertRaises(StrictCommitFailed, wt.commit,
351
391
message='add hello but not goodbye', strict=True)
353
393
def test_strict_commit_without_unknowns(self):
354
394
"""Try and commit with no unknown files and strict = True,
356
from bzrlib.errors import StrictCommitFailed
357
396
wt = self.make_branch_and_tree('.')
359
file('hello', 'w').write('hello world')
398
with open('hello', 'w') as f: f.write('hello world')
361
400
wt.commit(message='add hello', strict=True)
375
414
wt = self.make_branch_and_tree('.')
377
file('hello', 'w').write('hello world')
416
with open('hello', 'w') as f: f.write('hello world')
379
418
wt.commit(message='add hello', strict=False)
381
420
def test_signed_commit(self):
383
import bzrlib.commit as commit
384
oldstrategy = bzrlib.gpg.GPGStrategy
422
import breezy.commit as commit
423
oldstrategy = breezy.gpg.GPGStrategy
385
424
wt = self.make_branch_and_tree('.')
386
425
branch = wt.branch
387
426
wt.commit("base", allow_pointless=True, rev_id='A')
388
self.failIf(branch.repository.has_signature_for_revision_id('A'))
427
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
390
from bzrlib.testament import Testament
429
from ..testament import Testament
391
430
# 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,
431
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
432
conf = config.MemoryStack('''
433
create_signatures=always
435
commit.Commit(config_stack=conf).commit(
436
message="base", allow_pointless=True, rev_id='B',
398
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
439
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
399
440
self.assertEqual(sign(Testament.from_revision(branch.repository,
400
'B').as_short_text()),
441
'B').as_short_text()),
401
442
branch.repository.get_signature_text('B'))
403
bzrlib.gpg.GPGStrategy = oldstrategy
444
breezy.gpg.GPGStrategy = oldstrategy
405
446
def test_commit_failed_signature(self):
407
import bzrlib.commit as commit
408
oldstrategy = bzrlib.gpg.GPGStrategy
448
import breezy.commit as commit
449
oldstrategy = breezy.gpg.GPGStrategy
409
450
wt = self.make_branch_and_tree('.')
410
451
branch = wt.branch
411
452
wt.commit("base", allow_pointless=True, rev_id='A')
412
self.failIf(branch.repository.has_signature_for_revision_id('A'))
453
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
414
from bzrlib.testament import Testament
415
455
# 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,
456
breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
457
conf = config.MemoryStack('''
458
create_signatures=always
460
self.assertRaises(breezy.gpg.SigningFailed,
461
commit.Commit(config_stack=conf).commit,
421
463
allow_pointless=True,
424
466
branch = Branch.open(self.get_url('.'))
425
self.assertEqual(branch.revision_history(), ['A'])
426
self.failIf(branch.repository.has_revision('B'))
467
self.assertEqual(branch.last_revision(), 'A')
468
self.assertFalse(branch.repository.has_revision('B'))
428
bzrlib.gpg.GPGStrategy = oldstrategy
470
breezy.gpg.GPGStrategy = oldstrategy
430
472
def test_commit_invokes_hooks(self):
431
import bzrlib.commit as commit
473
import breezy.commit as commit
432
474
wt = self.make_branch_and_tree('.')
433
475
branch = wt.branch
435
477
def called(branch, rev_id):
436
478
calls.append('called')
437
bzrlib.ahook = called
479
breezy.ahook = called
439
config = BranchWithHooks(branch)
440
commit.Commit(config=config).commit(
442
allow_pointless=True,
443
rev_id='A', working_tree = wt)
481
conf = config.MemoryStack('post_commit=breezy.ahook breezy.ahook')
482
commit.Commit(config_stack=conf).commit(
483
message = "base", allow_pointless=True, rev_id='A',
444
485
self.assertEqual(['called', 'called'], calls)
448
489
def test_commit_object_doesnt_set_nick(self):
449
490
# using the Commit object directly does not set the branch nick.
450
491
wt = self.make_branch_and_tree('.')
452
493
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
453
self.assertEquals(wt.branch.revno(), 1)
494
self.assertEqual(wt.branch.revno(), 1)
454
495
self.assertEqual({},
455
496
wt.branch.repository.get_revision(
456
497
wt.branch.last_revision()).properties)
619
660
os.symlink('target', 'name')
620
661
tree.add('name', 'a-file-id')
621
662
tree.commit('Added a symlink')
622
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
663
self.assertBasisTreeKind('symlink', tree, 'name')
624
665
os.unlink('name')
625
666
self.build_tree(['name'])
626
667
tree.commit('Changed symlink to file')
627
self.assertBasisTreeKind('file', tree, 'a-file-id')
668
self.assertBasisTreeKind('file', tree, 'name')
629
670
os.unlink('name')
630
671
os.symlink('target', 'name')
631
672
tree.commit('file to symlink')
632
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
673
self.assertBasisTreeKind('symlink', tree, 'name')
634
675
os.unlink('name')
636
677
tree.commit('symlink to directory')
637
self.assertBasisTreeKind('directory', tree, 'a-file-id')
678
self.assertBasisTreeKind('directory', tree, 'name')
640
681
os.symlink('target', 'name')
641
682
tree.commit('directory to symlink')
642
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
683
self.assertBasisTreeKind('symlink', tree, 'name')
644
685
# prepare for directory <-> file tests
645
686
os.unlink('name')
647
688
tree.commit('symlink to directory')
648
self.assertBasisTreeKind('directory', tree, 'a-file-id')
689
self.assertBasisTreeKind('directory', tree, 'name')
651
692
self.build_tree(['name'])
652
693
tree.commit('Changed directory to file')
653
self.assertBasisTreeKind('file', tree, 'a-file-id')
694
self.assertBasisTreeKind('file', tree, 'name')
655
696
os.unlink('name')
657
698
tree.commit('file to directory')
658
self.assertBasisTreeKind('directory', tree, 'a-file-id')
699
self.assertBasisTreeKind('directory', tree, 'name')
660
701
def test_commit_unversioned_specified(self):
661
702
"""Commit should raise if specified files isn't in basis or worktree"""
751
792
self.assertFalse('authors' in rev.properties)
753
794
def test_commit_author(self):
754
"""Passing a non-empty author kwarg to MutableTree.commit should add
795
"""Passing a non-empty authors kwarg to MutableTree.commit should add
755
796
the 'author' revision property.
757
798
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>')
799
rev_id = tree.commit(
801
authors=['John Doe <jdoe@example.com>'])
761
802
rev = tree.branch.repository.get_revision(rev_id)
762
803
self.assertEqual('John Doe <jdoe@example.com>',
763
804
rev.properties['authors'])
781
822
'Jane Rey <jrey@example.com>', rev.properties['authors'])
782
823
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
825
def test_author_with_newline_rejected(self):
792
826
tree = self.make_branch_and_tree('foo')
793
827
self.assertRaises(AssertionError, tree.commit, 'commit 1',
796
830
def test_commit_with_checkout_and_branch_sharing_repo(self):
797
831
repo = self.make_repository('repo', shared=True)
798
832
# make_branch_and_tree ignores shared repos
799
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
833
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
800
834
tree2 = branch.create_checkout('repo/tree2')
801
835
tree2.commit('message', rev_id='rev1')
802
836
self.assertTrue(tree2.branch.repository.has_revision('rev1'))
839
class FilterExcludedTests(TestCase):
841
def test_add_file_not_excluded(self):
843
('fid', (None, 'newpath'),
844
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
845
('file', 'file'), (True, True))]
846
self.assertEqual(changes, list(filter_excluded(changes, ['otherpath'])))
848
def test_add_file_excluded(self):
850
('fid', (None, 'newpath'),
851
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
852
('file', 'file'), (True, True))]
853
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
855
def test_delete_file_excluded(self):
857
('fid', ('somepath', None),
858
0, (False, None), ('pid', None), ('newpath', None),
859
('file', None), (True, None))]
860
self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
862
def test_move_from_or_to_excluded(self):
864
('fid', ('oldpath', 'newpath'),
865
0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
866
('file', 'file'), (True, True))]
867
self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
868
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))