21
21
from bzrlib import (
28
26
from bzrlib.branch import Branch
29
from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1
27
from bzrlib.bzrdir import BzrDirMetaFormat1
30
28
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
29
from bzrlib.errors import (
35
from bzrlib.tests import (
36
TestCaseWithTransport,
39
from bzrlib.tests.features import (
42
from bzrlib.tests.matchers import MatchesAncestry
38
45
# 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"
47
class MustSignConfig(config.MemoryStack):
50
super(MustSignConfig, self).__init__('''
51
gpg_signing_command=cat -
52
create_signatures=always
55
56
class CapturingReporter(NullCommitReporter):
84
85
file('hello', 'w').write('hello world')
86
wt.commit(message='add hello')
87
rev1 = wt.commit(message='add hello')
87
88
file_id = wt.path2id('hello')
89
90
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
91
rev2 = wt.commit(message='commit 2')
92
93
eq = self.assertEquals
94
rh = b.revision_history()
95
rev = b.repository.get_revision(rh[0])
95
rev = b.repository.get_revision(rev1)
96
96
eq(rev.message, 'add hello')
98
tree1 = b.repository.revision_tree(rh[0])
98
tree1 = b.repository.revision_tree(rev1)
100
100
text = tree1.get_file_text(file_id)
102
102
self.assertEqual('hello world', text)
104
tree2 = b.repository.revision_tree(rh[1])
104
tree2 = b.repository.revision_tree(rev2)
105
105
tree2.lock_read()
106
106
text = tree2.get_file_text(file_id)
108
108
self.assertEqual('version 2', text)
110
def test_delete_commit(self):
111
"""Test a commit with a deleted file"""
110
def test_commit_lossy_native(self):
111
"""Attempt a lossy commit to a native branch."""
112
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
116
revid = wt.commit(message='add hello', rev_id='revid', lossy=True)
117
self.assertEquals('revid', revid)
119
def test_commit_lossy_foreign(self):
120
"""Attempt a lossy commit to a foreign branch."""
121
test_foreign.register_dummy_foreign_for_test(self)
122
wt = self.make_branch_and_tree('.',
123
format=test_foreign.DummyForeignVcsDirFormat())
125
file('hello', 'w').write('hello world')
127
revid = wt.commit(message='add hello', lossy=True,
128
timestamp=1302659388, timezone=0)
129
self.assertEquals('dummy-v1:1302659388.0-0-UNKNOWN', revid)
131
def test_commit_bound_lossy_foreign(self):
132
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
133
test_foreign.register_dummy_foreign_for_test(self)
134
foreign_branch = self.make_branch('foreign',
135
format=test_foreign.DummyForeignVcsDirFormat())
136
wt = foreign_branch.create_checkout("local")
138
file('local/hello', 'w').write('hello world')
140
revid = wt.commit(message='add hello', lossy=True,
141
timestamp=1302659388, timezone=0)
142
self.assertEquals('dummy-v1:1302659388.0-0-0', revid)
143
self.assertEquals('dummy-v1:1302659388.0-0-0',
144
foreign_branch.last_revision())
145
self.assertEquals('dummy-v1:1302659388.0-0-0',
146
wt.branch.last_revision())
148
def test_missing_commit(self):
149
"""Test a commit with a missing file"""
112
150
wt = self.make_branch_and_tree('.')
114
152
file('hello', 'w').write('hello world')
116
154
wt.commit(message='add hello')
118
156
os.remove('hello')
119
wt.commit('removed hello', rev_id='rev2')
157
reporter = CapturingReporter()
158
wt.commit('removed hello', rev_id='rev2', reporter=reporter)
160
[('missing', u'hello'), ('deleted', u'hello')],
121
163
tree = b.repository.revision_tree('rev2')
122
164
self.assertFalse(tree.has_id('hello-id'))
224
266
eq(tree1.id2path('hello-id'), 'hello')
225
267
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
226
268
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')
269
self.check_tree_shape(tree1, ['hello'])
270
eq(tree1.get_file_revision('hello-id'), 'test@rev-1')
231
272
tree2 = b.repository.revision_tree('test@rev-2')
232
273
tree2.lock_read()
233
274
self.addCleanup(tree2.unlock)
234
275
eq(tree2.id2path('hello-id'), 'fruity')
235
276
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')
277
self.check_tree_shape(tree2, ['fruity'])
278
eq(tree2.get_file_revision('hello-id'), 'test@rev-2')
240
280
def test_reused_rev_id(self):
241
281
"""Test that a revision id cannot be reused in a branch"""
272
311
wt.commit('three', rev_id=r3, allow_pointless=False)
275
self.check_inventory_shape(wt.read_working_inventory(),
314
self.check_tree_shape(wt,
276
315
['a/', 'a/hello', 'a/b/'])
277
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
316
self.check_tree_shape(b.repository.revision_tree(r3),
278
317
['a/', 'a/hello', 'a/b/'])
284
323
wt.commit('four', rev_id=r4, allow_pointless=False)
287
self.check_inventory_shape(wt.read_working_inventory(),
288
['a/', 'a/b/hello', 'a/b/'])
326
self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
292
inv = b.repository.get_revision_inventory(r4)
330
inv = b.repository.get_inventory(r4)
293
331
eq(inv['hello-id'].revision, r4)
294
332
eq(inv['a-id'].revision, r1)
295
333
eq(inv['b-id'].revision, r3)
320
358
rev_ids.append(rev_id)
321
359
wt.commit(message='rev %d' % (i+1),
323
eq = self.assertEquals
324
eq(b.revision_history(), rev_ids)
325
361
for i in range(4):
326
anc = b.repository.get_ancestry(rev_ids[i])
327
eq(anc, [None] + rev_ids[:i+1])
362
self.assertThat(rev_ids[:i+1],
363
MatchesAncestry(b.repository, rev_ids[i]))
329
365
def test_commit_new_subdir_child_selective(self):
330
366
wt = self.make_branch_and_tree('.')
385
420
wt = self.make_branch_and_tree('.')
386
421
branch = wt.branch
387
422
wt.commit("base", allow_pointless=True, rev_id='A')
388
self.failIf(branch.repository.has_signature_for_revision_id('A'))
423
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
390
425
from bzrlib.testament import Testament
391
426
# monkey patch gpg signing mechanism
392
427
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
393
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
394
allow_pointless=True,
428
conf = config.MemoryStack('''
429
gpg_signing_command=cat -
430
create_signatures=always
432
commit.Commit(config_stack=conf).commit(
433
message="base", allow_pointless=True, rev_id='B',
398
436
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
399
437
self.assertEqual(sign(Testament.from_revision(branch.repository,
400
'B').as_short_text()),
438
'B').as_short_text()),
401
439
branch.repository.get_signature_text('B'))
403
441
bzrlib.gpg.GPGStrategy = oldstrategy
409
447
wt = self.make_branch_and_tree('.')
410
448
branch = wt.branch
411
449
wt.commit("base", allow_pointless=True, rev_id='A')
412
self.failIf(branch.repository.has_signature_for_revision_id('A'))
450
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
414
from bzrlib.testament import Testament
415
452
# monkey patch gpg signing mechanism
416
453
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
417
config = MustSignConfig(branch)
454
conf = config.MemoryStack('''
455
gpg_signing_command=cat -
456
create_signatures=always
418
458
self.assertRaises(SigningFailed,
419
commit.Commit(config=config).commit,
459
commit.Commit(config_stack=conf).commit,
421
461
allow_pointless=True,
424
464
branch = Branch.open(self.get_url('.'))
425
self.assertEqual(branch.revision_history(), ['A'])
426
self.failIf(branch.repository.has_revision('B'))
465
self.assertEqual(branch.last_revision(), 'A')
466
self.assertFalse(branch.repository.has_revision('B'))
428
468
bzrlib.gpg.GPGStrategy = oldstrategy
436
476
calls.append('called')
437
477
bzrlib.ahook = called
439
config = BranchWithHooks(branch)
440
commit.Commit(config=config).commit(
442
allow_pointless=True,
443
rev_id='A', working_tree = wt)
479
conf = config.MemoryStack('post_commit=bzrlib.ahook bzrlib.ahook')
480
commit.Commit(config_stack=conf).commit(
481
message = "base", allow_pointless=True, rev_id='A',
444
483
self.assertEqual(['called', 'called'], calls)
550
589
this_tree.merge_from_branch(other_tree.branch)
551
590
reporter = CapturingReporter()
552
591
this_tree.commit('do the commit', reporter=reporter)
554
('change', 'unchanged', ''),
555
('change', 'unchanged', 'dirtoleave'),
556
('change', 'unchanged', 'filetoleave'),
557
593
('change', 'modified', 'filetomodify'),
558
594
('change', 'added', 'newdir'),
559
595
('change', 'added', 'newfile'),
563
599
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
564
600
('deleted', 'dirtoremove'),
565
601
('deleted', 'filetoremove'),
603
result = set(reporter.calls)
604
missing = expected - result
605
new = result - expected
606
self.assertEqual((set(), set()), (missing, new))
569
608
def test_commit_removals_respects_filespec(self):
570
609
"""Commit respects the specified_files for removals."""
660
699
def test_commit_unversioned_specified(self):
661
700
"""Commit should raise if specified files isn't in basis or worktree"""
662
701
tree = self.make_branch_and_tree('.')
663
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
702
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
664
703
'message', specific_files=['bogus'])
666
705
class Callback(object):
668
707
def __init__(self, message, testcase):
669
708
self.called = False
670
709
self.message = message
708
747
cb = self.Callback(u'commit 2', self)
709
748
repository = tree.branch.repository
710
749
# simulate network failure
711
def raise_(self, arg, arg2):
750
def raise_(self, arg, arg2, arg3=None, arg4=None):
712
751
raise errors.NoSuchFile('foo')
713
752
repository.add_inventory = raise_
753
repository.add_inventory_by_delta = raise_
714
754
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
715
755
self.assertFalse(cb.called)
747
787
rev_id = tree.commit('commit 1')
748
788
rev = tree.branch.repository.get_revision(rev_id)
749
789
self.assertFalse('author' in rev.properties)
790
self.assertFalse('authors' in rev.properties)
751
792
def test_commit_author(self):
752
793
"""Passing a non-empty author kwarg to MutableTree.commit should add
753
794
the 'author' revision property.
755
796
tree = self.make_branch_and_tree('foo')
756
rev_id = tree.commit('commit 1', author='John Doe <jdoe@example.com>')
797
rev_id = self.callDeprecated(['The parameter author was '
798
'deprecated in version 1.13. Use authors instead'],
799
tree.commit, 'commit 1', author='John Doe <jdoe@example.com>')
757
800
rev = tree.branch.repository.get_revision(rev_id)
758
801
self.assertEqual('John Doe <jdoe@example.com>',
759
rev.properties['author'])
802
rev.properties['authors'])
803
self.assertFalse('author' in rev.properties)
805
def test_commit_empty_authors_list(self):
806
"""Passing an empty list to authors shouldn't add the property."""
807
tree = self.make_branch_and_tree('foo')
808
rev_id = tree.commit('commit 1', authors=[])
809
rev = tree.branch.repository.get_revision(rev_id)
810
self.assertFalse('author' in rev.properties)
811
self.assertFalse('authors' in rev.properties)
813
def test_multiple_authors(self):
814
tree = self.make_branch_and_tree('foo')
815
rev_id = tree.commit('commit 1',
816
authors=['John Doe <jdoe@example.com>',
817
'Jane Rey <jrey@example.com>'])
818
rev = tree.branch.repository.get_revision(rev_id)
819
self.assertEqual('John Doe <jdoe@example.com>\n'
820
'Jane Rey <jrey@example.com>', rev.properties['authors'])
821
self.assertFalse('author' in rev.properties)
823
def test_author_and_authors_incompatible(self):
824
tree = self.make_branch_and_tree('foo')
825
self.assertRaises(AssertionError, tree.commit, 'commit 1',
826
authors=['John Doe <jdoe@example.com>',
827
'Jane Rey <jrey@example.com>'],
828
author="Jack Me <jme@example.com>")
830
def test_author_with_newline_rejected(self):
831
tree = self.make_branch_and_tree('foo')
832
self.assertRaises(AssertionError, tree.commit, 'commit 1',
833
authors=['John\nDoe <jdoe@example.com>'])
761
835
def test_commit_with_checkout_and_branch_sharing_repo(self):
762
836
repo = self.make_repository('repo', shared=True)