1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
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
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
50
# TODO: Test commit with some added, and added-but-missing files
52
class MustSignConfig(config.MemoryStack):
55
super(MustSignConfig, self).__init__('''
56
create_signatures=always
60
class CapturingReporter(NullCommitReporter):
61
"""This reporter captures the calls made to it for evaluation later."""
64
# a list of the calls this received
67
def snapshot_change(self, change, path):
68
self.calls.append(('change', change, path))
70
def deleted(self, file_id):
71
self.calls.append(('deleted', file_id))
73
def missing(self, path):
74
self.calls.append(('missing', path))
76
def renamed(self, change, old_path, new_path):
77
self.calls.append(('renamed', change, old_path, new_path))
83
class TestCommit(TestCaseWithTransport):
85
def test_simple_commit(self):
86
"""Commit and check two versions of a single file."""
87
wt = self.make_branch_and_tree('.')
89
with open('hello', 'w') as f: f.write('hello world')
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')
98
rev = b.repository.get_revision(rev1)
99
eq(rev.message, 'add hello')
101
tree1 = b.repository.revision_tree(rev1)
103
text = tree1.get_file_text('hello')
105
self.assertEqual('hello world', text)
107
tree2 = b.repository.revision_tree(rev2)
109
text = tree2.get_file_text('hello')
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())
151
def test_missing_commit(self):
152
"""Test a commit with a missing file"""
153
wt = self.make_branch_and_tree('.')
155
with open('hello', 'w') as f: f.write('hello world')
156
wt.add(['hello'], ['hello-id'])
157
wt.commit(message='add hello')
160
reporter = CapturingReporter()
161
wt.commit('removed hello', rev_id='rev2', reporter=reporter)
163
[('missing', u'hello'), ('deleted', u'hello')],
166
tree = b.repository.revision_tree('rev2')
167
self.assertFalse(tree.has_id('hello-id'))
169
def test_partial_commit_move(self):
170
"""Test a partial commit where a file was renamed but not committed.
172
https://bugs.launchpad.net/bzr/+bug/83039
174
If not handled properly, commit will try to snapshot
175
dialog.py with olive/ as a parent, while
176
olive/ has not been snapshotted yet.
178
wt = self.make_branch_and_tree('.')
180
self.build_tree(['annotate/', 'annotate/foo.py',
181
'olive/', 'olive/dialog.py'
183
wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
184
wt.commit(message='add files')
185
wt.rename_one("olive/dialog.py", "aaa")
186
self.build_tree_contents([('annotate/foo.py', 'modified\n')])
187
wt.commit('renamed hello', specific_files=["annotate"])
189
def test_pointless_commit(self):
190
"""Commit refuses unless there are changes or it's forced."""
191
wt = self.make_branch_and_tree('.')
193
with open('hello', 'w') as f: f.write('hello')
195
wt.commit(message='add hello')
196
self.assertEqual(b.revno(), 1)
197
self.assertRaises(PointlessCommit,
200
allow_pointless=False)
201
self.assertEqual(b.revno(), 1)
203
def test_commit_empty(self):
204
"""Commiting an empty tree works."""
205
wt = self.make_branch_and_tree('.')
207
wt.commit(message='empty tree', allow_pointless=True)
208
self.assertRaises(PointlessCommit,
210
message='empty tree',
211
allow_pointless=False)
212
wt.commit(message='empty tree', allow_pointless=True)
213
self.assertEqual(b.revno(), 2)
215
def test_selective_delete(self):
216
"""Selective commit in tree with deletions"""
217
wt = self.make_branch_and_tree('.')
219
with open('hello', 'w') as f: f.write('hello')
220
with open('buongia', 'w') as f: f.write('buongia')
221
wt.add(['hello', 'buongia'],
222
['hello-id', 'buongia-id'])
223
wt.commit(message='add files',
227
with open('buongia', 'w') as f: f.write('new text')
228
wt.commit(message='update text',
229
specific_files=['buongia'],
230
allow_pointless=False,
233
wt.commit(message='remove hello',
234
specific_files=['hello'],
235
allow_pointless=False,
238
eq = self.assertEqual
241
tree2 = b.repository.revision_tree('test@rev-2')
243
self.addCleanup(tree2.unlock)
244
self.assertTrue(tree2.has_filename('hello'))
245
self.assertEqual(tree2.get_file_text('hello'), 'hello')
246
self.assertEqual(tree2.get_file_text('buongia'), 'new text')
248
tree3 = b.repository.revision_tree('test@rev-3')
250
self.addCleanup(tree3.unlock)
251
self.assertFalse(tree3.has_filename('hello'))
252
self.assertEqual(tree3.get_file_text('buongia'), 'new text')
254
def test_commit_rename(self):
255
"""Test commit of a revision where a file is renamed."""
256
tree = self.make_branch_and_tree('.')
258
self.build_tree(['hello'], line_endings='binary')
259
tree.add(['hello'], ['hello-id'])
260
tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
262
tree.rename_one('hello', 'fruity')
263
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
265
eq = self.assertEqual
266
tree1 = b.repository.revision_tree('test@rev-1')
268
self.addCleanup(tree1.unlock)
269
eq(tree1.id2path('hello-id'), 'hello')
270
eq(tree1.get_file_text('hello'), 'contents of hello\n')
271
self.assertFalse(tree1.has_filename('fruity'))
272
self.check_tree_shape(tree1, ['hello'])
273
eq(tree1.get_file_revision('hello'), 'test@rev-1')
275
tree2 = b.repository.revision_tree('test@rev-2')
277
self.addCleanup(tree2.unlock)
278
eq(tree2.id2path('hello-id'), 'fruity')
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')
283
def test_reused_rev_id(self):
284
"""Test that a revision id cannot be reused in a branch"""
285
wt = self.make_branch_and_tree('.')
287
wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
288
self.assertRaises(Exception,
292
allow_pointless=True)
294
def test_commit_move(self):
295
"""Test commit of revisions with moved files and directories"""
296
eq = self.assertEqual
297
wt = self.make_branch_and_tree('.')
300
self.build_tree(['hello', 'a/', 'b/'])
301
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
302
wt.commit('initial', rev_id=r1, allow_pointless=False)
303
wt.move(['hello'], 'a')
305
wt.commit('two', rev_id=r2, allow_pointless=False)
308
self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
314
wt.commit('three', rev_id=r3, allow_pointless=False)
317
self.check_tree_shape(wt,
318
['a/', 'a/hello', 'a/b/'])
319
self.check_tree_shape(b.repository.revision_tree(r3),
320
['a/', 'a/hello', 'a/b/'])
324
wt.move(['a/hello'], 'a/b')
326
wt.commit('four', rev_id=r4, allow_pointless=False)
329
self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
333
inv = b.repository.get_inventory(r4)
334
eq(inv['hello-id'].revision, r4)
335
eq(inv['a-id'].revision, r1)
336
eq(inv['b-id'].revision, r3)
338
def test_removed_commit(self):
339
"""Commit with a removed file"""
340
wt = self.make_branch_and_tree('.')
342
with open('hello', 'w') as f: f.write('hello world')
343
wt.add(['hello'], ['hello-id'])
344
wt.commit(message='add hello')
346
wt.commit('removed hello', rev_id='rev2')
348
tree = b.repository.revision_tree('rev2')
349
self.assertFalse(tree.has_id('hello-id'))
351
def test_committed_ancestry(self):
352
"""Test commit appends revisions to ancestry."""
353
wt = self.make_branch_and_tree('.')
357
with open('hello', 'w') as f: f.write((str(i) * 4) + '\n')
359
wt.add(['hello'], ['hello-id'])
360
rev_id = 'test@rev-%d' % (i+1)
361
rev_ids.append(rev_id)
362
wt.commit(message='rev %d' % (i+1),
365
self.assertThat(rev_ids[:i+1],
366
MatchesAncestry(b.repository, rev_ids[i]))
368
def test_commit_new_subdir_child_selective(self):
369
wt = self.make_branch_and_tree('.')
371
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
372
wt.add(['dir', 'dir/file1', 'dir/file2'],
373
['dirid', 'file1id', 'file2id'])
374
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
375
inv = b.repository.get_inventory('1')
376
self.assertEqual('1', inv['dirid'].revision)
377
self.assertEqual('1', inv['file1id'].revision)
378
# FIXME: This should raise a KeyError I think, rbc20051006
379
self.assertRaises(BzrError, inv.__getitem__, 'file2id')
381
def test_strict_commit(self):
382
"""Try and commit with unknown files and strict = True, should fail."""
383
from ..errors import StrictCommitFailed
384
wt = self.make_branch_and_tree('.')
386
with open('hello', 'w') as f: f.write('hello world')
388
with open('goodbye', 'w') as f: f.write('goodbye cruel world!')
389
self.assertRaises(StrictCommitFailed, wt.commit,
390
message='add hello but not goodbye', strict=True)
392
def test_strict_commit_without_unknowns(self):
393
"""Try and commit with no unknown files and strict = True,
395
wt = self.make_branch_and_tree('.')
397
with open('hello', 'w') as f: f.write('hello world')
399
wt.commit(message='add hello', strict=True)
401
def test_nonstrict_commit(self):
402
"""Try and commit with unknown files and strict = False, should work."""
403
wt = self.make_branch_and_tree('.')
405
with open('hello', 'w') as f: f.write('hello world')
407
with open('goodbye', 'w') as f: f.write('goodbye cruel world!')
408
wt.commit(message='add hello but not goodbye', strict=False)
410
def test_nonstrict_commit_without_unknowns(self):
411
"""Try and commit with no unknown files and strict = False,
413
wt = self.make_branch_and_tree('.')
415
with open('hello', 'w') as f: f.write('hello world')
417
wt.commit(message='add hello', strict=False)
419
def test_signed_commit(self):
421
import breezy.commit as commit
422
oldstrategy = breezy.gpg.GPGStrategy
423
wt = self.make_branch_and_tree('.')
425
wt.commit("base", allow_pointless=True, rev_id='A')
426
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
428
from ..testament import Testament
429
# monkey patch gpg signing mechanism
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',
438
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
439
self.assertEqual(sign(Testament.from_revision(branch.repository,
440
'B').as_short_text()),
441
branch.repository.get_signature_text('B'))
443
breezy.gpg.GPGStrategy = oldstrategy
445
def test_commit_failed_signature(self):
447
import breezy.commit as commit
448
oldstrategy = breezy.gpg.GPGStrategy
449
wt = self.make_branch_and_tree('.')
451
wt.commit("base", allow_pointless=True, rev_id='A')
452
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
454
# monkey patch gpg signing mechanism
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,
462
allow_pointless=True,
465
branch = Branch.open(self.get_url('.'))
466
self.assertEqual(branch.last_revision(), 'A')
467
self.assertFalse(branch.repository.has_revision('B'))
469
breezy.gpg.GPGStrategy = oldstrategy
471
def test_commit_invokes_hooks(self):
472
import breezy.commit as commit
473
wt = self.make_branch_and_tree('.')
476
def called(branch, rev_id):
477
calls.append('called')
478
breezy.ahook = called
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',
484
self.assertEqual(['called', 'called'], calls)
488
def test_commit_object_doesnt_set_nick(self):
489
# using the Commit object directly does not set the branch nick.
490
wt = self.make_branch_and_tree('.')
492
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
493
self.assertEqual(wt.branch.revno(), 1)
495
wt.branch.repository.get_revision(
496
wt.branch.last_revision()).properties)
498
def test_safe_master_lock(self):
500
master = BzrDirMetaFormat1().initialize('master')
501
master.create_repository()
502
master_branch = master.create_branch()
503
master.create_workingtree()
504
bound = master.sprout('bound')
505
wt = bound.open_workingtree()
506
wt.branch.set_bound_location(os.path.realpath('master'))
507
master_branch.lock_write()
509
self.assertRaises(LockContention, wt.commit, 'silly')
511
master_branch.unlock()
513
def test_commit_bound_merge(self):
514
# see bug #43959; commit of a merge in a bound branch fails to push
515
# the new commit into the master
516
master_branch = self.make_branch('master')
517
bound_tree = self.make_branch_and_tree('bound')
518
bound_tree.branch.bind(master_branch)
520
self.build_tree_contents([('bound/content_file', 'initial contents\n')])
521
bound_tree.add(['content_file'])
522
bound_tree.commit(message='woo!')
524
other_bzrdir = master_branch.controldir.sprout('other')
525
other_tree = other_bzrdir.open_workingtree()
527
# do a commit to the other branch changing the content file so
528
# that our commit after merging will have a merged revision in the
529
# content file history.
530
self.build_tree_contents([('other/content_file', 'change in other\n')])
531
other_tree.commit('change in other')
533
# do a merge into the bound branch from other, and then change the
534
# content file locally to force a new revision (rather than using the
535
# revision from other). This forces extra processing in commit.
536
bound_tree.merge_from_branch(other_tree.branch)
537
self.build_tree_contents([('bound/content_file', 'change in bound\n')])
539
# before #34959 was fixed, this failed with 'revision not present in
540
# weave' when trying to implicitly push from the bound branch to the master
541
bound_tree.commit(message='commit of merge in bound tree')
543
def test_commit_reporting_after_merge(self):
544
# when doing a commit of a merge, the reporter needs to still
545
# be called for each item that is added/removed/deleted.
546
this_tree = self.make_branch_and_tree('this')
547
# we need a bunch of files and dirs, to perform one action on each.
550
'this/dirtoreparent/',
553
'this/filetoreparent',
570
this_tree.commit('create_files')
571
other_dir = this_tree.controldir.sprout('other')
572
other_tree = other_dir.open_workingtree()
573
other_tree.lock_write()
574
# perform the needed actions on the files and dirs.
576
other_tree.rename_one('dirtorename', 'renameddir')
577
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
578
other_tree.rename_one('filetorename', 'renamedfile')
579
other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
580
other_tree.remove(['dirtoremove', 'filetoremove'])
581
self.build_tree_contents([
583
('other/filetomodify', 'new content'),
584
('other/newfile', 'new file content')])
585
other_tree.add('newfile')
586
other_tree.add('newdir/')
587
other_tree.commit('modify all sample files and dirs.')
590
this_tree.merge_from_branch(other_tree.branch)
591
reporter = CapturingReporter()
592
this_tree.commit('do the commit', reporter=reporter)
594
('change', 'modified', 'filetomodify'),
595
('change', 'added', 'newdir'),
596
('change', 'added', 'newfile'),
597
('renamed', 'renamed', 'dirtorename', 'renameddir'),
598
('renamed', 'renamed', 'filetorename', 'renamedfile'),
599
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
600
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
601
('deleted', 'dirtoremove'),
602
('deleted', 'filetoremove'),
604
result = set(reporter.calls)
605
missing = expected - result
606
new = result - expected
607
self.assertEqual((set(), set()), (missing, new))
609
def test_commit_removals_respects_filespec(self):
610
"""Commit respects the specified_files for removals."""
611
tree = self.make_branch_and_tree('.')
612
self.build_tree(['a', 'b'])
614
tree.commit('added a, b')
615
tree.remove(['a', 'b'])
616
tree.commit('removed a', specific_files='a')
617
basis = tree.basis_tree()
618
with tree.lock_read():
619
self.assertFalse(basis.is_versioned('a'))
620
self.assertTrue(basis.is_versioned('b'))
622
def test_commit_saves_1ms_timestamp(self):
623
"""Passing in a timestamp is saved with 1ms resolution"""
624
tree = self.make_branch_and_tree('.')
625
self.build_tree(['a'])
627
tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
630
rev = tree.branch.repository.get_revision('a1')
631
self.assertEqual(1153248633.419, rev.timestamp)
633
def test_commit_has_1ms_resolution(self):
634
"""Allowing commit to generate the timestamp also has 1ms resolution"""
635
tree = self.make_branch_and_tree('.')
636
self.build_tree(['a'])
638
tree.commit('added a', rev_id='a1')
640
rev = tree.branch.repository.get_revision('a1')
641
timestamp = rev.timestamp
642
timestamp_1ms = round(timestamp, 3)
643
self.assertEqual(timestamp_1ms, timestamp)
645
def assertBasisTreeKind(self, kind, tree, path):
646
basis = tree.basis_tree()
649
self.assertEqual(kind, basis.kind(path))
653
def test_commit_kind_changes(self):
654
self.requireFeature(SymlinkFeature)
655
tree = self.make_branch_and_tree('.')
656
os.symlink('target', 'name')
657
tree.add('name', 'a-file-id')
658
tree.commit('Added a symlink')
659
self.assertBasisTreeKind('symlink', tree, 'name')
662
self.build_tree(['name'])
663
tree.commit('Changed symlink to file')
664
self.assertBasisTreeKind('file', tree, 'name')
667
os.symlink('target', 'name')
668
tree.commit('file to symlink')
669
self.assertBasisTreeKind('symlink', tree, 'name')
673
tree.commit('symlink to directory')
674
self.assertBasisTreeKind('directory', tree, 'name')
677
os.symlink('target', 'name')
678
tree.commit('directory to symlink')
679
self.assertBasisTreeKind('symlink', tree, 'name')
681
# prepare for directory <-> file tests
684
tree.commit('symlink to directory')
685
self.assertBasisTreeKind('directory', tree, 'name')
688
self.build_tree(['name'])
689
tree.commit('Changed directory to file')
690
self.assertBasisTreeKind('file', tree, 'name')
694
tree.commit('file to directory')
695
self.assertBasisTreeKind('directory', tree, 'name')
697
def test_commit_unversioned_specified(self):
698
"""Commit should raise if specified files isn't in basis or worktree"""
699
tree = self.make_branch_and_tree('.')
700
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
701
'message', specific_files=['bogus'])
703
class Callback(object):
705
def __init__(self, message, testcase):
707
self.message = message
708
self.testcase = testcase
710
def __call__(self, commit_obj):
712
self.testcase.assertTrue(isinstance(commit_obj, Commit))
715
def test_commit_callback(self):
716
"""Commit should invoke a callback to get the message"""
718
tree = self.make_branch_and_tree('.')
721
except Exception as e:
722
self.assertTrue(isinstance(e, BzrError))
723
self.assertEqual('The message or message_callback keyword'
724
' parameter is required for commit().', str(e))
726
self.fail('exception not raised')
727
cb = self.Callback(u'commit 1', self)
728
tree.commit(message_callback=cb)
729
self.assertTrue(cb.called)
730
repository = tree.branch.repository
731
message = repository.get_revision(tree.last_revision()).message
732
self.assertEqual('commit 1', message)
734
def test_no_callback_pointless(self):
735
"""Callback should not be invoked for pointless commit"""
736
tree = self.make_branch_and_tree('.')
737
cb = self.Callback(u'commit 2', self)
738
self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
739
allow_pointless=False)
740
self.assertFalse(cb.called)
742
def test_no_callback_netfailure(self):
743
"""Callback should not be invoked if connectivity fails"""
744
tree = self.make_branch_and_tree('.')
745
cb = self.Callback(u'commit 2', self)
746
repository = tree.branch.repository
747
# simulate network failure
748
def raise_(self, arg, arg2, arg3=None, arg4=None):
749
raise errors.NoSuchFile('foo')
750
repository.add_inventory = raise_
751
repository.add_inventory_by_delta = raise_
752
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
753
self.assertFalse(cb.called)
755
def test_selected_file_merge_commit(self):
756
"""Ensure the correct error is raised"""
757
tree = self.make_branch_and_tree('foo')
758
# pending merge would turn into a left parent
759
tree.commit('commit 1')
760
tree.add_parent_tree_id('example')
761
self.build_tree(['foo/bar', 'foo/baz'])
762
tree.add(['bar', 'baz'])
763
err = self.assertRaises(CannotCommitSelectedFileMerge,
764
tree.commit, 'commit 2', specific_files=['bar', 'baz'])
765
self.assertEqual(['bar', 'baz'], err.files)
766
self.assertEqual('Selected-file commit of merges is not supported'
767
' yet: files bar, baz', str(err))
769
def test_commit_ordering(self):
770
"""Test of corner-case commit ordering error"""
771
tree = self.make_branch_and_tree('.')
772
self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
773
tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
775
self.build_tree(['a/c/d/'])
777
tree.rename_one('a/z/x', 'a/c/d/x')
778
tree.commit('test', specific_files=['a/z/y'])
780
def test_commit_no_author(self):
781
"""The default kwarg author in MutableTree.commit should not add
782
the 'author' revision property.
784
tree = self.make_branch_and_tree('foo')
785
rev_id = tree.commit('commit 1')
786
rev = tree.branch.repository.get_revision(rev_id)
787
self.assertFalse('author' in rev.properties)
788
self.assertFalse('authors' in rev.properties)
790
def test_commit_author(self):
791
"""Passing a non-empty authors kwarg to MutableTree.commit should add
792
the 'author' revision property.
794
tree = self.make_branch_and_tree('foo')
795
rev_id = tree.commit(
797
authors=['John Doe <jdoe@example.com>'])
798
rev = tree.branch.repository.get_revision(rev_id)
799
self.assertEqual('John Doe <jdoe@example.com>',
800
rev.properties['authors'])
801
self.assertFalse('author' in rev.properties)
803
def test_commit_empty_authors_list(self):
804
"""Passing an empty list to authors shouldn't add the property."""
805
tree = self.make_branch_and_tree('foo')
806
rev_id = tree.commit('commit 1', authors=[])
807
rev = tree.branch.repository.get_revision(rev_id)
808
self.assertFalse('author' in rev.properties)
809
self.assertFalse('authors' in rev.properties)
811
def test_multiple_authors(self):
812
tree = self.make_branch_and_tree('foo')
813
rev_id = tree.commit('commit 1',
814
authors=['John Doe <jdoe@example.com>',
815
'Jane Rey <jrey@example.com>'])
816
rev = tree.branch.repository.get_revision(rev_id)
817
self.assertEqual('John Doe <jdoe@example.com>\n'
818
'Jane Rey <jrey@example.com>', rev.properties['authors'])
819
self.assertFalse('author' in rev.properties)
821
def test_author_with_newline_rejected(self):
822
tree = self.make_branch_and_tree('foo')
823
self.assertRaises(AssertionError, tree.commit, 'commit 1',
824
authors=['John\nDoe <jdoe@example.com>'])
826
def test_commit_with_checkout_and_branch_sharing_repo(self):
827
repo = self.make_repository('repo', shared=True)
828
# make_branch_and_tree ignores shared repos
829
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
830
tree2 = branch.create_checkout('repo/tree2')
831
tree2.commit('message', rev_id='rev1')
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'])))