89
49
"""Commit and check two versions of a single file."""
90
50
wt = self.make_branch_and_tree('.')
92
with open('hello', 'w') as f:
93
f.write('hello world')
52
file('hello', 'w').write('hello world')
95
rev1 = wt.commit(message='add hello')
97
with open('hello', 'w') as f:
99
rev2 = wt.commit(message='commit 2')
101
eq = self.assertEqual
54
wt.commit(message='add hello')
55
file_id = wt.path2id('hello')
57
file('hello', 'w').write('version 2')
58
wt.commit(message='commit 2')
60
eq = self.assertEquals
103
rev = b.repository.get_revision(rev1)
62
rh = b.revision_history()
63
rev = b.repository.get_revision(rh[0])
104
64
eq(rev.message, 'add hello')
106
tree1 = b.repository.revision_tree(rev1)
108
text = tree1.get_file_text('hello')
110
self.assertEqual(b'hello world', text)
112
tree2 = b.repository.revision_tree(rev2)
114
text = tree2.get_file_text('hello')
116
self.assertEqual(b'version 2', text)
118
def test_commit_lossy_native(self):
119
"""Attempt a lossy commit to a native branch."""
120
wt = self.make_branch_and_tree('.')
122
with open('hello', 'w') as f:
123
f.write('hello world')
125
revid = wt.commit(message='add hello', rev_id=b'revid', lossy=True)
126
self.assertEqual(b'revid', revid)
128
def test_commit_lossy_foreign(self):
129
"""Attempt a lossy commit to a foreign branch."""
130
test_foreign.register_dummy_foreign_for_test(self)
131
wt = self.make_branch_and_tree('.',
132
format=test_foreign.DummyForeignVcsDirFormat())
134
with open('hello', 'w') as f:
135
f.write('hello world')
137
revid = wt.commit(message='add hello', lossy=True,
138
timestamp=1302659388, timezone=0)
139
self.assertEqual(b'dummy-v1:1302659388-0-UNKNOWN', revid)
141
def test_commit_bound_lossy_foreign(self):
142
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
143
test_foreign.register_dummy_foreign_for_test(self)
144
foreign_branch = self.make_branch('foreign',
145
format=test_foreign.DummyForeignVcsDirFormat())
146
wt = foreign_branch.create_checkout("local")
148
with open('local/hello', 'w') as f:
149
f.write('hello world')
151
revid = wt.commit(message='add hello', lossy=True,
152
timestamp=1302659388, timezone=0)
153
self.assertEqual(b'dummy-v1:1302659388-0-0', revid)
154
self.assertEqual(b'dummy-v1:1302659388-0-0',
155
foreign_branch.last_revision())
156
self.assertEqual(b'dummy-v1:1302659388-0-0',
157
wt.branch.last_revision())
159
def test_missing_commit(self):
160
"""Test a commit with a missing file"""
161
wt = self.make_branch_and_tree('.')
163
with open('hello', 'w') as f:
164
f.write('hello world')
165
wt.add(['hello'], [b'hello-id'])
66
tree1 = b.repository.revision_tree(rh[0])
67
text = tree1.get_file_text(file_id)
68
eq(text, 'hello world')
70
tree2 = b.repository.revision_tree(rh[1])
71
eq(tree2.get_file_text(file_id), 'version 2')
73
def test_delete_commit(self):
74
"""Test a commit with a deleted file"""
75
wt = self.make_branch_and_tree('.')
77
file('hello', 'w').write('hello world')
78
wt.add(['hello'], ['hello-id'])
166
79
wt.commit(message='add hello')
168
81
os.remove('hello')
169
reporter = CapturingReporter()
170
wt.commit('removed hello', rev_id=b'rev2', reporter=reporter)
172
[('missing', u'hello'), ('deleted', u'hello')],
175
tree = b.repository.revision_tree(b'rev2')
176
self.assertFalse(tree.has_filename('hello'))
178
def test_partial_commit_move(self):
179
"""Test a partial commit where a file was renamed but not committed.
181
https://bugs.launchpad.net/bzr/+bug/83039
183
If not handled properly, commit will try to snapshot
184
dialog.py with olive/ as a parent, while
185
olive/ has not been snapshotted yet.
187
wt = self.make_branch_and_tree('.')
189
self.build_tree(['annotate/', 'annotate/foo.py',
190
'olive/', 'olive/dialog.py'
192
wt.add(['annotate', 'olive', 'annotate/foo.py', 'olive/dialog.py'])
193
wt.commit(message='add files')
194
wt.rename_one("olive/dialog.py", "aaa")
195
self.build_tree_contents([('annotate/foo.py', b'modified\n')])
196
wt.commit('renamed hello', specific_files=["annotate"])
82
wt.commit('removed hello', rev_id='rev2')
84
tree = b.repository.revision_tree('rev2')
85
self.assertFalse(tree.has_id('hello-id'))
198
87
def test_pointless_commit(self):
199
88
"""Commit refuses unless there are changes or it's forced."""
200
89
wt = self.make_branch_and_tree('.')
202
with open('hello', 'w') as f:
91
file('hello', 'w').write('hello')
205
93
wt.commit(message='add hello')
206
self.assertEqual(b.revno(), 1)
94
self.assertEquals(b.revno(), 1)
207
95
self.assertRaises(PointlessCommit,
210
98
allow_pointless=False)
211
self.assertEqual(b.revno(), 1)
99
self.assertEquals(b.revno(), 1)
213
101
def test_commit_empty(self):
214
102
"""Commiting an empty tree works."""
215
103
wt = self.make_branch_and_tree('.')
220
108
message='empty tree',
221
109
allow_pointless=False)
222
110
wt.commit(message='empty tree', allow_pointless=True)
223
self.assertEqual(b.revno(), 2)
111
self.assertEquals(b.revno(), 2)
225
113
def test_selective_delete(self):
226
114
"""Selective commit in tree with deletions"""
227
115
wt = self.make_branch_and_tree('.')
229
with open('hello', 'w') as f:
231
with open('buongia', 'w') as f:
117
file('hello', 'w').write('hello')
118
file('buongia', 'w').write('buongia')
233
119
wt.add(['hello', 'buongia'],
234
[b'hello-id', b'buongia-id'])
120
['hello-id', 'buongia-id'])
235
121
wt.commit(message='add files',
236
rev_id=b'test@rev-1')
238
124
os.remove('hello')
239
with open('buongia', 'w') as f:
125
file('buongia', 'w').write('new text')
241
126
wt.commit(message='update text',
242
specific_files=['buongia'],
243
allow_pointless=False,
244
rev_id=b'test@rev-2')
127
specific_files=['buongia'],
128
allow_pointless=False,
246
131
wt.commit(message='remove hello',
247
specific_files=['hello'],
248
allow_pointless=False,
249
rev_id=b'test@rev-3')
132
specific_files=['hello'],
133
allow_pointless=False,
251
eq = self.assertEqual
136
eq = self.assertEquals
254
tree2 = b.repository.revision_tree(b'test@rev-2')
256
self.addCleanup(tree2.unlock)
139
tree2 = b.repository.revision_tree('test@rev-2')
257
140
self.assertTrue(tree2.has_filename('hello'))
258
self.assertEqual(tree2.get_file_text('hello'), b'hello')
259
self.assertEqual(tree2.get_file_text('buongia'), b'new text')
261
tree3 = b.repository.revision_tree(b'test@rev-3')
263
self.addCleanup(tree3.unlock)
141
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
142
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
144
tree3 = b.repository.revision_tree('test@rev-3')
264
145
self.assertFalse(tree3.has_filename('hello'))
265
self.assertEqual(tree3.get_file_text('buongia'), b'new text')
146
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
267
148
def test_commit_rename(self):
268
149
"""Test commit of a revision where a file is renamed."""
269
150
tree = self.make_branch_and_tree('.')
271
152
self.build_tree(['hello'], line_endings='binary')
272
tree.add(['hello'], [b'hello-id'])
273
tree.commit(message='one', rev_id=b'test@rev-1', allow_pointless=False)
153
tree.add(['hello'], ['hello-id'])
154
tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
275
156
tree.rename_one('hello', 'fruity')
276
tree.commit(message='renamed', rev_id=b'test@rev-2',
277
allow_pointless=False)
157
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
279
eq = self.assertEqual
280
tree1 = b.repository.revision_tree(b'test@rev-1')
282
self.addCleanup(tree1.unlock)
283
eq(tree1.id2path(b'hello-id'), 'hello')
284
eq(tree1.get_file_text('hello'), b'contents of hello\n')
159
eq = self.assertEquals
160
tree1 = b.repository.revision_tree('test@rev-1')
161
eq(tree1.id2path('hello-id'), 'hello')
162
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
285
163
self.assertFalse(tree1.has_filename('fruity'))
286
self.check_tree_shape(tree1, ['hello'])
287
eq(tree1.get_file_revision('hello'), b'test@rev-1')
164
self.check_inventory_shape(tree1.inventory, ['hello'])
165
ie = tree1.inventory['hello-id']
166
eq(ie.revision, 'test@rev-1')
289
tree2 = b.repository.revision_tree(b'test@rev-2')
291
self.addCleanup(tree2.unlock)
292
eq(tree2.id2path(b'hello-id'), 'fruity')
293
eq(tree2.get_file_text('fruity'), b'contents of hello\n')
294
self.check_tree_shape(tree2, ['fruity'])
295
eq(tree2.get_file_revision('fruity'), b'test@rev-2')
168
tree2 = b.repository.revision_tree('test@rev-2')
169
eq(tree2.id2path('hello-id'), 'fruity')
170
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
171
self.check_inventory_shape(tree2.inventory, ['fruity'])
172
ie = tree2.inventory['hello-id']
173
eq(ie.revision, 'test@rev-2')
297
175
def test_reused_rev_id(self):
298
176
"""Test that a revision id cannot be reused in a branch"""
299
177
wt = self.make_branch_and_tree('.')
301
wt.commit('initial', rev_id=b'test@rev-1', allow_pointless=True)
179
wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
302
180
self.assertRaises(Exception,
304
182
message='reused id',
305
rev_id=b'test@rev-1',
306
184
allow_pointless=True)
308
186
def test_commit_move(self):
309
187
"""Test commit of revisions with moved files and directories"""
310
eq = self.assertEqual
188
eq = self.assertEquals
311
189
wt = self.make_branch_and_tree('.')
314
192
self.build_tree(['hello', 'a/', 'b/'])
315
wt.add(['hello', 'a', 'b'], [b'hello-id', b'a-id', b'b-id'])
193
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
316
194
wt.commit('initial', rev_id=r1, allow_pointless=False)
317
195
wt.move(['hello'], 'a')
319
197
wt.commit('two', rev_id=r2, allow_pointless=False)
322
self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
198
self.check_inventory_shape(wt.read_working_inventory(),
199
['a', 'a/hello', 'b'])
326
201
wt.move(['b'], 'a')
328
203
wt.commit('three', rev_id=r3, allow_pointless=False)
331
self.check_tree_shape(wt,
332
['a/', 'a/hello', 'a/b/'])
333
self.check_tree_shape(b.repository.revision_tree(r3),
334
['a/', 'a/hello', 'a/b/'])
204
self.check_inventory_shape(wt.read_working_inventory(),
205
['a', 'a/hello', 'a/b'])
206
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
207
['a', 'a/hello', 'a/b'])
338
209
wt.move(['a/hello'], 'a/b')
340
211
wt.commit('four', rev_id=r4, allow_pointless=False)
343
self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
347
inv = b.repository.get_inventory(r4)
348
eq(inv.get_entry(b'hello-id').revision, r4)
349
eq(inv.get_entry(b'a-id').revision, r1)
350
eq(inv.get_entry(b'b-id').revision, r3)
212
self.check_inventory_shape(wt.read_working_inventory(),
213
['a', 'a/b/hello', 'a/b'])
215
inv = b.repository.get_revision_inventory(r4)
216
eq(inv['hello-id'].revision, r4)
217
eq(inv['a-id'].revision, r1)
218
eq(inv['b-id'].revision, r3)
352
220
def test_removed_commit(self):
353
221
"""Commit with a removed file"""
354
222
wt = self.make_branch_and_tree('.')
356
with open('hello', 'w') as f:
357
f.write('hello world')
358
wt.add(['hello'], [b'hello-id'])
224
file('hello', 'w').write('hello world')
225
wt.add(['hello'], ['hello-id'])
359
226
wt.commit(message='add hello')
360
227
wt.remove('hello')
361
wt.commit('removed hello', rev_id=b'rev2')
228
wt.commit('removed hello', rev_id='rev2')
363
tree = b.repository.revision_tree(b'rev2')
364
self.assertFalse(tree.has_filename('hello'))
230
tree = b.repository.revision_tree('rev2')
231
self.assertFalse(tree.has_id('hello-id'))
366
233
def test_committed_ancestry(self):
367
234
"""Test commit appends revisions to ancestry."""
434
298
wt = self.make_branch_and_tree('.')
436
with open('hello', 'w') as f:
437
f.write('hello world')
300
file('hello', 'w').write('hello world')
439
302
wt.commit(message='add hello', strict=False)
441
304
def test_signed_commit(self):
443
import breezy.commit as commit
444
oldstrategy = breezy.gpg.GPGStrategy
306
import bzrlib.commit as commit
307
oldstrategy = bzrlib.gpg.GPGStrategy
445
308
wt = self.make_branch_and_tree('.')
446
309
branch = wt.branch
447
wt.commit("base", allow_pointless=True, rev_id=b'A')
448
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
310
wt.commit("base", allow_pointless=True, rev_id='A')
311
self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
450
from ..bzr.testament import Testament
313
from bzrlib.testament import Testament
451
314
# monkey patch gpg signing mechanism
452
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
453
conf = config.MemoryStack(b'''
454
create_signatures=always
456
commit.Commit(config_stack=conf).commit(
457
message="base", allow_pointless=True, rev_id=b'B',
461
return breezy.gpg.LoopbackGPGStrategy(None).sign(
462
text, breezy.gpg.MODE_CLEAR)
463
self.assertEqual(sign(Testament.from_revision(branch.repository,
464
b'B').as_short_text()),
465
branch.repository.get_signature_text(b'B'))
315
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
316
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
317
allow_pointless=True,
320
self.assertEqual(Testament.from_revision(branch.repository,
321
'B').as_short_text(),
322
branch.repository.revision_store.get('B',
467
breezy.gpg.GPGStrategy = oldstrategy
325
bzrlib.gpg.GPGStrategy = oldstrategy
469
327
def test_commit_failed_signature(self):
471
import breezy.commit as commit
472
oldstrategy = breezy.gpg.GPGStrategy
329
import bzrlib.commit as commit
330
oldstrategy = bzrlib.gpg.GPGStrategy
473
331
wt = self.make_branch_and_tree('.')
474
332
branch = wt.branch
475
wt.commit("base", allow_pointless=True, rev_id=b'A')
476
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
333
wt.commit("base", allow_pointless=True, rev_id='A')
334
self.failIf(branch.repository.revision_store.has_id('A', 'sig'))
336
from bzrlib.testament import Testament
478
337
# monkey patch gpg signing mechanism
479
breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
480
conf = config.MemoryStack(b'''
481
create_signatures=always
483
self.assertRaises(breezy.gpg.SigningFailed,
484
commit.Commit(config_stack=conf).commit,
338
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
339
config = MustSignConfig(branch)
340
self.assertRaises(SigningFailed,
341
commit.Commit(config=config).commit,
486
343
allow_pointless=True,
489
346
branch = Branch.open(self.get_url('.'))
490
self.assertEqual(branch.last_revision(), b'A')
491
self.assertFalse(branch.repository.has_revision(b'B'))
347
self.assertEqual(branch.revision_history(), ['A'])
348
self.failIf(branch.repository.revision_store.has_id('B'))
493
breezy.gpg.GPGStrategy = oldstrategy
350
bzrlib.gpg.GPGStrategy = oldstrategy
495
352
def test_commit_invokes_hooks(self):
496
import breezy.commit as commit
353
import bzrlib.commit as commit
497
354
wt = self.make_branch_and_tree('.')
498
355
branch = wt.branch
501
357
def called(branch, rev_id):
502
358
calls.append('called')
503
breezy.ahook = called
359
bzrlib.ahook = called
505
conf = config.MemoryStack(b'post_commit=breezy.ahook breezy.ahook')
506
commit.Commit(config_stack=conf).commit(
507
message="base", allow_pointless=True, rev_id=b'A',
361
config = BranchWithHooks(branch)
362
commit.Commit(config=config).commit(
364
allow_pointless=True,
365
rev_id='A', working_tree = wt)
509
366
self.assertEqual(['called', 'called'], calls)
513
def test_commit_object_doesnt_set_nick(self):
514
# using the Commit object directly does not set the branch nick.
515
wt = self.make_branch_and_tree('.')
517
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
518
self.assertEqual(wt.branch.revno(), 1)
520
wt.branch.repository.get_revision(
521
wt.branch.last_revision()).properties)
523
def test_safe_master_lock(self):
525
master = BzrDirMetaFormat1().initialize('master')
526
master.create_repository()
527
master_branch = master.create_branch()
528
master.create_workingtree()
529
bound = master.sprout('bound')
530
wt = bound.open_workingtree()
531
wt.branch.set_bound_location(os.path.realpath('master'))
532
master_branch.lock_write()
534
self.assertRaises(LockContention, wt.commit, 'silly')
536
master_branch.unlock()
538
def test_commit_bound_merge(self):
539
# see bug #43959; commit of a merge in a bound branch fails to push
540
# the new commit into the master
541
master_branch = self.make_branch('master')
542
bound_tree = self.make_branch_and_tree('bound')
543
bound_tree.branch.bind(master_branch)
545
self.build_tree_contents(
546
[('bound/content_file', b'initial contents\n')])
547
bound_tree.add(['content_file'])
548
bound_tree.commit(message='woo!')
550
other_bzrdir = master_branch.controldir.sprout('other')
551
other_tree = other_bzrdir.open_workingtree()
553
# do a commit to the other branch changing the content file so
554
# that our commit after merging will have a merged revision in the
555
# content file history.
556
self.build_tree_contents(
557
[('other/content_file', b'change in other\n')])
558
other_tree.commit('change in other')
560
# do a merge into the bound branch from other, and then change the
561
# content file locally to force a new revision (rather than using the
562
# revision from other). This forces extra processing in commit.
563
bound_tree.merge_from_branch(other_tree.branch)
564
self.build_tree_contents(
565
[('bound/content_file', b'change in bound\n')])
567
# before #34959 was fixed, this failed with 'revision not present in
568
# weave' when trying to implicitly push from the bound branch to the master
569
bound_tree.commit(message='commit of merge in bound tree')
571
def test_commit_reporting_after_merge(self):
572
# when doing a commit of a merge, the reporter needs to still
573
# be called for each item that is added/removed/deleted.
574
this_tree = self.make_branch_and_tree('this')
575
# we need a bunch of files and dirs, to perform one action on each.
578
'this/dirtoreparent/',
581
'this/filetoreparent',
598
this_tree.commit('create_files')
599
other_dir = this_tree.controldir.sprout('other')
600
other_tree = other_dir.open_workingtree()
601
other_tree.lock_write()
602
# perform the needed actions on the files and dirs.
604
other_tree.rename_one('dirtorename', 'renameddir')
605
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
606
other_tree.rename_one('filetorename', 'renamedfile')
607
other_tree.rename_one(
608
'filetoreparent', 'renameddir/reparentedfile')
609
other_tree.remove(['dirtoremove', 'filetoremove'])
610
self.build_tree_contents([
612
('other/filetomodify', b'new content'),
613
('other/newfile', b'new file content')])
614
other_tree.add('newfile')
615
other_tree.add('newdir/')
616
other_tree.commit('modify all sample files and dirs.')
619
this_tree.merge_from_branch(other_tree.branch)
620
reporter = CapturingReporter()
621
this_tree.commit('do the commit', reporter=reporter)
623
('change', 'modified', 'filetomodify'),
624
('change', 'added', 'newdir'),
625
('change', 'added', 'newfile'),
626
('renamed', 'renamed', 'dirtorename', 'renameddir'),
627
('renamed', 'renamed', 'filetorename', 'renamedfile'),
628
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
629
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
630
('deleted', 'dirtoremove'),
631
('deleted', 'filetoremove'),
633
result = set(reporter.calls)
634
missing = expected - result
635
new = result - expected
636
self.assertEqual((set(), set()), (missing, new))
638
def test_commit_removals_respects_filespec(self):
639
"""Commit respects the specified_files for removals."""
640
tree = self.make_branch_and_tree('.')
641
self.build_tree(['a', 'b'])
643
tree.commit('added a, b')
644
tree.remove(['a', 'b'])
645
tree.commit('removed a', specific_files='a')
646
basis = tree.basis_tree()
647
with tree.lock_read():
648
self.assertFalse(basis.is_versioned('a'))
649
self.assertTrue(basis.is_versioned('b'))
651
def test_commit_saves_1ms_timestamp(self):
652
"""Passing in a timestamp is saved with 1ms resolution"""
653
tree = self.make_branch_and_tree('.')
654
self.build_tree(['a'])
656
tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
659
rev = tree.branch.repository.get_revision(b'a1')
660
self.assertEqual(1153248633.419, rev.timestamp)
662
def test_commit_has_1ms_resolution(self):
663
"""Allowing commit to generate the timestamp also has 1ms resolution"""
664
tree = self.make_branch_and_tree('.')
665
self.build_tree(['a'])
667
tree.commit('added a', rev_id=b'a1')
669
rev = tree.branch.repository.get_revision(b'a1')
670
timestamp = rev.timestamp
671
timestamp_1ms = round(timestamp, 3)
672
self.assertEqual(timestamp_1ms, timestamp)
674
def assertBasisTreeKind(self, kind, tree, path):
675
basis = tree.basis_tree()
678
self.assertEqual(kind, basis.kind(path))
682
def test_unsupported_symlink_commit(self):
683
self.requireFeature(SymlinkFeature)
684
tree = self.make_branch_and_tree('.')
685
self.build_tree(['hello'])
687
tree.commit('added hello', rev_id=b'hello_id')
688
os.symlink('hello', 'foo')
690
tree.commit('added foo', rev_id=b'foo_id')
692
trace.push_log_file(log)
693
os_symlink = getattr(os, 'symlink', None)
696
# At this point as bzr thinks symlinks are not supported
697
# we should get a warning about symlink foo and bzr should
698
# not think its removed.
700
self.build_tree(['world'])
702
tree.commit('added world', rev_id=b'world_id')
705
os.symlink = os_symlink
706
self.assertContainsRe(
708
b'Ignoring "foo" as symlinks are not '
709
b'supported on this filesystem\\.')
711
def test_commit_kind_changes(self):
712
self.requireFeature(SymlinkFeature)
713
tree = self.make_branch_and_tree('.')
714
os.symlink('target', 'name')
715
tree.add('name', b'a-file-id')
716
tree.commit('Added a symlink')
717
self.assertBasisTreeKind('symlink', tree, 'name')
720
self.build_tree(['name'])
721
tree.commit('Changed symlink to file')
722
self.assertBasisTreeKind('file', tree, 'name')
725
os.symlink('target', 'name')
726
tree.commit('file to symlink')
727
self.assertBasisTreeKind('symlink', tree, 'name')
731
tree.commit('symlink to directory')
732
self.assertBasisTreeKind('directory', tree, 'name')
735
os.symlink('target', 'name')
736
tree.commit('directory to symlink')
737
self.assertBasisTreeKind('symlink', tree, 'name')
739
# prepare for directory <-> file tests
742
tree.commit('symlink to directory')
743
self.assertBasisTreeKind('directory', tree, 'name')
746
self.build_tree(['name'])
747
tree.commit('Changed directory to file')
748
self.assertBasisTreeKind('file', tree, 'name')
752
tree.commit('file to directory')
753
self.assertBasisTreeKind('directory', tree, 'name')
755
def test_commit_unversioned_specified(self):
756
"""Commit should raise if specified files isn't in basis or worktree"""
757
tree = self.make_branch_and_tree('.')
758
self.assertRaises(errors.PathsNotVersionedError, tree.commit,
759
'message', specific_files=['bogus'])
761
class Callback(object):
763
def __init__(self, message, testcase):
765
self.message = message
766
self.testcase = testcase
768
def __call__(self, commit_obj):
770
self.testcase.assertTrue(isinstance(commit_obj, Commit))
773
def test_commit_callback(self):
774
"""Commit should invoke a callback to get the message"""
776
tree = self.make_branch_and_tree('.')
779
except Exception as e:
780
self.assertTrue(isinstance(e, BzrError))
781
self.assertEqual('The message or message_callback keyword'
782
' parameter is required for commit().', str(e))
784
self.fail('exception not raised')
785
cb = self.Callback(u'commit 1', self)
786
tree.commit(message_callback=cb)
787
self.assertTrue(cb.called)
788
repository = tree.branch.repository
789
message = repository.get_revision(tree.last_revision()).message
790
self.assertEqual('commit 1', message)
792
def test_no_callback_pointless(self):
793
"""Callback should not be invoked for pointless commit"""
794
tree = self.make_branch_and_tree('.')
795
cb = self.Callback(u'commit 2', self)
796
self.assertRaises(PointlessCommit, tree.commit, message_callback=cb,
797
allow_pointless=False)
798
self.assertFalse(cb.called)
800
def test_no_callback_netfailure(self):
801
"""Callback should not be invoked if connectivity fails"""
802
tree = self.make_branch_and_tree('.')
803
cb = self.Callback(u'commit 2', self)
804
repository = tree.branch.repository
805
# simulate network failure
807
def raise_(self, arg, arg2, arg3=None, arg4=None):
808
raise errors.NoSuchFile('foo')
809
repository.add_inventory = raise_
810
repository.add_inventory_by_delta = raise_
811
self.assertRaises(errors.NoSuchFile, tree.commit, message_callback=cb)
812
self.assertFalse(cb.called)
814
def test_selected_file_merge_commit(self):
815
"""Ensure the correct error is raised"""
816
tree = self.make_branch_and_tree('foo')
817
# pending merge would turn into a left parent
818
tree.commit('commit 1')
819
tree.add_parent_tree_id(b'example')
820
self.build_tree(['foo/bar', 'foo/baz'])
821
tree.add(['bar', 'baz'])
822
err = self.assertRaises(CannotCommitSelectedFileMerge,
823
tree.commit, 'commit 2', specific_files=['bar', 'baz'])
824
self.assertEqual(['bar', 'baz'], err.files)
825
self.assertEqual('Selected-file commit of merges is not supported'
826
' yet: files bar, baz', str(err))
828
def test_commit_ordering(self):
829
"""Test of corner-case commit ordering error"""
830
tree = self.make_branch_and_tree('.')
831
self.build_tree(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
832
tree.add(['a/', 'a/z/', 'a/c/', 'a/z/x', 'a/z/y'])
834
self.build_tree(['a/c/d/'])
836
tree.rename_one('a/z/x', 'a/c/d/x')
837
tree.commit('test', specific_files=['a/z/y'])
839
def test_commit_no_author(self):
840
"""The default kwarg author in MutableTree.commit should not add
841
the 'author' revision property.
843
tree = self.make_branch_and_tree('foo')
844
rev_id = tree.commit('commit 1')
845
rev = tree.branch.repository.get_revision(rev_id)
846
self.assertFalse('author' in rev.properties)
847
self.assertFalse('authors' in rev.properties)
849
def test_commit_author(self):
850
"""Passing a non-empty authors kwarg to MutableTree.commit should add
851
the 'author' revision property.
853
tree = self.make_branch_and_tree('foo')
854
rev_id = tree.commit(
856
authors=['John Doe <jdoe@example.com>'])
857
rev = tree.branch.repository.get_revision(rev_id)
858
self.assertEqual('John Doe <jdoe@example.com>',
859
rev.properties['authors'])
860
self.assertFalse('author' in rev.properties)
862
def test_commit_empty_authors_list(self):
863
"""Passing an empty list to authors shouldn't add the property."""
864
tree = self.make_branch_and_tree('foo')
865
rev_id = tree.commit('commit 1', authors=[])
866
rev = tree.branch.repository.get_revision(rev_id)
867
self.assertFalse('author' in rev.properties)
868
self.assertFalse('authors' in rev.properties)
870
def test_multiple_authors(self):
871
tree = self.make_branch_and_tree('foo')
872
rev_id = tree.commit('commit 1',
873
authors=['John Doe <jdoe@example.com>',
874
'Jane Rey <jrey@example.com>'])
875
rev = tree.branch.repository.get_revision(rev_id)
876
self.assertEqual('John Doe <jdoe@example.com>\n'
877
'Jane Rey <jrey@example.com>', rev.properties['authors'])
878
self.assertFalse('author' in rev.properties)
880
def test_author_with_newline_rejected(self):
881
tree = self.make_branch_and_tree('foo')
882
self.assertRaises(AssertionError, tree.commit, 'commit 1',
883
authors=['John\nDoe <jdoe@example.com>'])
885
def test_commit_with_checkout_and_branch_sharing_repo(self):
886
repo = self.make_repository('repo', shared=True)
887
# make_branch_and_tree ignores shared repos
888
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
889
tree2 = branch.create_checkout('repo/tree2')
890
tree2.commit('message', rev_id=b'rev1')
891
self.assertTrue(tree2.branch.repository.has_revision(b'rev1'))
894
class FilterExcludedTests(TestCase):
896
def test_add_file_not_excluded(self):
899
'fid', (None, 'newpath'),
900
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
901
('file', 'file'), (True, True))]
902
self.assertEqual(changes, list(
903
filter_excluded(changes, ['otherpath'])))
905
def test_add_file_excluded(self):
908
'fid', (None, 'newpath'),
909
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
910
('file', 'file'), (True, True))]
911
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
913
def test_delete_file_excluded(self):
916
'fid', ('somepath', None),
917
0, (False, None), ('pid', None), ('newpath', None),
918
('file', None), (True, None))]
919
self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
921
def test_move_from_or_to_excluded(self):
924
'fid', ('oldpath', 'newpath'),
925
0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
926
('file', 'file'), (True, True))]
927
self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
928
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))