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')
87
file_id = wt.path2id('hello')
89
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
92
eq = self.assertEquals
91
rev1 = wt.commit(message='add hello')
93
with open('hello', 'w') as f: f.write('version 2')
94
rev2 = wt.commit(message='commit 2')
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
text = tree1.get_file_text(file_id)
103
text = tree1.get_file_text('hello')
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
text = tree2.get_file_text(file_id)
109
text = tree2.get_file_text('hello')
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 open('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 open('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 open('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 open('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 open('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 open('hello', 'w') as f: f.write('hello')
220
with open('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 open('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'), 'hello')
246
self.assertEqual(tree2.get_file_text('buongia'), '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'), '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
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
270
eq(tree1.get_file_text('hello'), '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'), '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
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')
279
eq(tree2.get_file_text('fruity'), 'contents of hello\n')
280
self.check_tree_shape(tree2, ['fruity'])
281
eq(tree2.get_file_revision('fruity'), '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 open('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 open('hello', 'w') as f: f.write('hello world')
349
file('goodbye', 'w').write('goodbye cruel world!')
388
with open('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 open('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 open('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)
619
656
os.symlink('target', 'name')
620
657
tree.add('name', 'a-file-id')
621
658
tree.commit('Added a symlink')
622
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
659
self.assertBasisTreeKind('symlink', tree, 'name')
624
661
os.unlink('name')
625
662
self.build_tree(['name'])
626
663
tree.commit('Changed symlink to file')
627
self.assertBasisTreeKind('file', tree, 'a-file-id')
664
self.assertBasisTreeKind('file', tree, 'name')
629
666
os.unlink('name')
630
667
os.symlink('target', 'name')
631
668
tree.commit('file to symlink')
632
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
669
self.assertBasisTreeKind('symlink', tree, 'name')
634
671
os.unlink('name')
636
673
tree.commit('symlink to directory')
637
self.assertBasisTreeKind('directory', tree, 'a-file-id')
674
self.assertBasisTreeKind('directory', tree, 'name')
640
677
os.symlink('target', 'name')
641
678
tree.commit('directory to symlink')
642
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
679
self.assertBasisTreeKind('symlink', tree, 'name')
644
681
# prepare for directory <-> file tests
645
682
os.unlink('name')
647
684
tree.commit('symlink to directory')
648
self.assertBasisTreeKind('directory', tree, 'a-file-id')
685
self.assertBasisTreeKind('directory', tree, 'name')
651
688
self.build_tree(['name'])
652
689
tree.commit('Changed directory to file')
653
self.assertBasisTreeKind('file', tree, 'a-file-id')
690
self.assertBasisTreeKind('file', tree, 'name')
655
692
os.unlink('name')
657
694
tree.commit('file to directory')
658
self.assertBasisTreeKind('directory', tree, 'a-file-id')
695
self.assertBasisTreeKind('directory', tree, 'name')
660
697
def test_commit_unversioned_specified(self):
661
698
"""Commit should raise if specified files isn't in basis or worktree"""
751
788
self.assertFalse('authors' in rev.properties)
753
790
def test_commit_author(self):
754
"""Passing a non-empty author kwarg to MutableTree.commit should add
791
"""Passing a non-empty authors kwarg to MutableTree.commit should add
755
792
the 'author' revision property.
757
794
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>')
795
rev_id = tree.commit(
797
authors=['John Doe <jdoe@example.com>'])
761
798
rev = tree.branch.repository.get_revision(rev_id)
762
799
self.assertEqual('John Doe <jdoe@example.com>',
763
800
rev.properties['authors'])
781
818
'Jane Rey <jrey@example.com>', rev.properties['authors'])
782
819
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
821
def test_author_with_newline_rejected(self):
792
822
tree = self.make_branch_and_tree('foo')
793
823
self.assertRaises(AssertionError, tree.commit, 'commit 1',
796
826
def test_commit_with_checkout_and_branch_sharing_repo(self):
797
827
repo = self.make_repository('repo', shared=True)
798
828
# make_branch_and_tree ignores shared repos
799
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
829
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
800
830
tree2 = branch.create_checkout('repo/tree2')
801
831
tree2.commit('message', rev_id='rev1')
802
832
self.assertTrue(tree2.branch.repository.has_revision('rev1'))
835
class FilterExcludedTests(TestCase):
837
def test_add_file_not_excluded(self):
839
('fid', (None, 'newpath'),
840
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
841
('file', 'file'), (True, True))]
842
self.assertEqual(changes, list(filter_excluded(changes, ['otherpath'])))
844
def test_add_file_excluded(self):
846
('fid', (None, 'newpath'),
847
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
848
('file', 'file'), (True, True))]
849
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
851
def test_delete_file_excluded(self):
853
('fid', ('somepath', None),
854
0, (False, None), ('pid', None), ('newpath', None),
855
('file', None), (True, None))]
856
self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
858
def test_move_from_or_to_excluded(self):
860
('fid', ('oldpath', 'newpath'),
861
0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
862
('file', 'file'), (True, True))]
863
self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
864
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))