89
71
"""Commit and check two versions of a single file."""
90
72
wt = self.make_branch_and_tree('.')
92
with open('hello', 'w') as f:
93
f.write('hello world')
74
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
76
wt.commit(message='add hello')
77
file_id = wt.path2id('hello')
79
file('hello', 'w').write('version 2')
80
wt.commit(message='commit 2')
82
eq = self.assertEquals
103
rev = b.repository.get_revision(rev1)
84
rh = b.revision_history()
85
rev = b.repository.get_revision(rh[0])
104
86
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'])
88
tree1 = b.repository.revision_tree(rh[0])
89
text = tree1.get_file_text(file_id)
90
eq(text, 'hello world')
92
tree2 = b.repository.revision_tree(rh[1])
93
eq(tree2.get_file_text(file_id), 'version 2')
95
def test_delete_commit(self):
96
"""Test a commit with a deleted file"""
97
wt = self.make_branch_and_tree('.')
99
file('hello', 'w').write('hello world')
100
wt.add(['hello'], ['hello-id'])
166
101
wt.commit(message='add hello')
168
103
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"])
104
wt.commit('removed hello', rev_id='rev2')
106
tree = b.repository.revision_tree('rev2')
107
self.assertFalse(tree.has_id('hello-id'))
198
109
def test_pointless_commit(self):
199
110
"""Commit refuses unless there are changes or it's forced."""
200
111
wt = self.make_branch_and_tree('.')
202
with open('hello', 'w') as f:
113
file('hello', 'w').write('hello')
204
114
wt.add(['hello'])
205
115
wt.commit(message='add hello')
206
self.assertEqual(b.revno(), 1)
116
self.assertEquals(b.revno(), 1)
207
117
self.assertRaises(PointlessCommit,
210
120
allow_pointless=False)
211
self.assertEqual(b.revno(), 1)
121
self.assertEquals(b.revno(), 1)
213
123
def test_commit_empty(self):
214
124
"""Commiting an empty tree works."""
215
125
wt = self.make_branch_and_tree('.')
220
130
message='empty tree',
221
131
allow_pointless=False)
222
132
wt.commit(message='empty tree', allow_pointless=True)
223
self.assertEqual(b.revno(), 2)
133
self.assertEquals(b.revno(), 2)
225
135
def test_selective_delete(self):
226
136
"""Selective commit in tree with deletions"""
227
137
wt = self.make_branch_and_tree('.')
229
with open('hello', 'w') as f:
231
with open('buongia', 'w') as f:
139
file('hello', 'w').write('hello')
140
file('buongia', 'w').write('buongia')
233
141
wt.add(['hello', 'buongia'],
234
[b'hello-id', b'buongia-id'])
142
['hello-id', 'buongia-id'])
235
143
wt.commit(message='add files',
236
rev_id=b'test@rev-1')
238
146
os.remove('hello')
239
with open('buongia', 'w') as f:
147
file('buongia', 'w').write('new text')
241
148
wt.commit(message='update text',
242
specific_files=['buongia'],
243
allow_pointless=False,
244
rev_id=b'test@rev-2')
149
specific_files=['buongia'],
150
allow_pointless=False,
246
153
wt.commit(message='remove hello',
247
specific_files=['hello'],
248
allow_pointless=False,
249
rev_id=b'test@rev-3')
154
specific_files=['hello'],
155
allow_pointless=False,
251
eq = self.assertEqual
158
eq = self.assertEquals
254
tree2 = b.repository.revision_tree(b'test@rev-2')
256
self.addCleanup(tree2.unlock)
161
tree2 = b.repository.revision_tree('test@rev-2')
257
162
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)
163
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
164
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
166
tree3 = b.repository.revision_tree('test@rev-3')
264
167
self.assertFalse(tree3.has_filename('hello'))
265
self.assertEqual(tree3.get_file_text('buongia'), b'new text')
168
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
267
170
def test_commit_rename(self):
268
171
"""Test commit of a revision where a file is renamed."""
269
172
tree = self.make_branch_and_tree('.')
271
174
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)
175
tree.add(['hello'], ['hello-id'])
176
tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
275
178
tree.rename_one('hello', 'fruity')
276
tree.commit(message='renamed', rev_id=b'test@rev-2',
277
allow_pointless=False)
179
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')
181
eq = self.assertEquals
182
tree1 = b.repository.revision_tree('test@rev-1')
183
eq(tree1.id2path('hello-id'), 'hello')
184
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
285
185
self.assertFalse(tree1.has_filename('fruity'))
286
self.check_tree_shape(tree1, ['hello'])
287
eq(tree1.get_file_revision('hello'), b'test@rev-1')
186
self.check_inventory_shape(tree1.inventory, ['hello'])
187
ie = tree1.inventory['hello-id']
188
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')
190
tree2 = b.repository.revision_tree('test@rev-2')
191
eq(tree2.id2path('hello-id'), 'fruity')
192
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
193
self.check_inventory_shape(tree2.inventory, ['fruity'])
194
ie = tree2.inventory['hello-id']
195
eq(ie.revision, 'test@rev-2')
297
197
def test_reused_rev_id(self):
298
198
"""Test that a revision id cannot be reused in a branch"""
299
199
wt = self.make_branch_and_tree('.')
301
wt.commit('initial', rev_id=b'test@rev-1', allow_pointless=True)
201
wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
302
202
self.assertRaises(Exception,
304
204
message='reused id',
305
rev_id=b'test@rev-1',
306
206
allow_pointless=True)
308
208
def test_commit_move(self):
309
209
"""Test commit of revisions with moved files and directories"""
310
eq = self.assertEqual
210
eq = self.assertEquals
311
211
wt = self.make_branch_and_tree('.')
314
214
self.build_tree(['hello', 'a/', 'b/'])
315
wt.add(['hello', 'a', 'b'], [b'hello-id', b'a-id', b'b-id'])
215
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
316
216
wt.commit('initial', rev_id=r1, allow_pointless=False)
317
217
wt.move(['hello'], 'a')
319
219
wt.commit('two', rev_id=r2, allow_pointless=False)
322
self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
220
self.check_inventory_shape(wt.read_working_inventory(),
221
['a', 'a/hello', 'b'])
326
223
wt.move(['b'], 'a')
328
225
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/'])
226
self.check_inventory_shape(wt.read_working_inventory(),
227
['a', 'a/hello', 'a/b'])
228
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
229
['a', 'a/hello', 'a/b'])
338
231
wt.move(['a/hello'], 'a/b')
340
233
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)
234
self.check_inventory_shape(wt.read_working_inventory(),
235
['a', 'a/b/hello', 'a/b'])
237
inv = b.repository.get_revision_inventory(r4)
238
eq(inv['hello-id'].revision, r4)
239
eq(inv['a-id'].revision, r1)
240
eq(inv['b-id'].revision, r3)
352
242
def test_removed_commit(self):
353
243
"""Commit with a removed file"""
354
244
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'])
246
file('hello', 'w').write('hello world')
247
wt.add(['hello'], ['hello-id'])
359
248
wt.commit(message='add hello')
360
249
wt.remove('hello')
361
wt.commit('removed hello', rev_id=b'rev2')
250
wt.commit('removed hello', rev_id='rev2')
363
tree = b.repository.revision_tree(b'rev2')
364
self.assertFalse(tree.has_filename('hello'))
252
tree = b.repository.revision_tree('rev2')
253
self.assertFalse(tree.has_id('hello-id'))
366
255
def test_committed_ancestry(self):
367
256
"""Test commit appends revisions to ancestry."""
371
260
for i in range(4):
372
with open('hello', 'w') as f:
373
f.write((str(i) * 4) + '\n')
261
file('hello', 'w').write((str(i) * 4) + '\n')
375
wt.add(['hello'], [b'hello-id'])
376
rev_id = b'test@rev-%d' % (i + 1)
263
wt.add(['hello'], ['hello-id'])
264
rev_id = 'test@rev-%d' % (i+1)
377
265
rev_ids.append(rev_id)
378
wt.commit(message='rev %d' % (i + 1),
266
wt.commit(message='rev %d' % (i+1),
268
eq = self.assertEquals
269
eq(b.revision_history(), rev_ids)
380
270
for i in range(4):
381
self.assertThat(rev_ids[:i + 1],
382
MatchesAncestry(b.repository, rev_ids[i]))
271
anc = b.repository.get_ancestry(rev_ids[i])
272
eq(anc, [None] + rev_ids[:i+1])
384
274
def test_commit_new_subdir_child_selective(self):
385
275
wt = self.make_branch_and_tree('.')
387
277
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
388
278
wt.add(['dir', 'dir/file1', 'dir/file2'],
389
[b'dirid', b'file1id', b'file2id'])
390
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id=b'1')
391
inv = b.repository.get_inventory(b'1')
392
self.assertEqual(b'1', inv.get_entry(b'dirid').revision)
393
self.assertEqual(b'1', inv.get_entry(b'file1id').revision)
279
['dirid', 'file1id', 'file2id'])
280
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
281
inv = b.repository.get_inventory('1')
282
self.assertEqual('1', inv['dirid'].revision)
283
self.assertEqual('1', inv['file1id'].revision)
394
284
# FIXME: This should raise a KeyError I think, rbc20051006
395
self.assertRaises(BzrError, inv.get_entry, b'file2id')
285
self.assertRaises(BzrError, inv.__getitem__, 'file2id')
397
287
def test_strict_commit(self):
398
288
"""Try and commit with unknown files and strict = True, should fail."""
399
from ..errors import StrictCommitFailed
289
from bzrlib.errors import StrictCommitFailed
400
290
wt = self.make_branch_and_tree('.')
402
with open('hello', 'w') as f:
403
f.write('hello world')
292
file('hello', 'w').write('hello world')
405
with open('goodbye', 'w') as f:
406
f.write('goodbye cruel world!')
294
file('goodbye', 'w').write('goodbye cruel world!')
407
295
self.assertRaises(StrictCommitFailed, wt.commit,
408
message='add hello but not goodbye', strict=True)
296
message='add hello but not goodbye', strict=True)
410
298
def test_strict_commit_without_unknowns(self):
411
299
"""Try and commit with no unknown files and strict = True,
301
from bzrlib.errors import StrictCommitFailed
413
302
wt = self.make_branch_and_tree('.')
415
with open('hello', 'w') as f:
416
f.write('hello world')
304
file('hello', 'w').write('hello world')
418
306
wt.commit(message='add hello', strict=True)
434
320
wt = self.make_branch_and_tree('.')
436
with open('hello', 'w') as f:
437
f.write('hello world')
322
file('hello', 'w').write('hello world')
439
324
wt.commit(message='add hello', strict=False)
441
326
def test_signed_commit(self):
443
import breezy.commit as commit
444
oldstrategy = breezy.gpg.GPGStrategy
328
import bzrlib.commit as commit
329
oldstrategy = bzrlib.gpg.GPGStrategy
445
330
wt = self.make_branch_and_tree('.')
446
331
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'))
332
wt.commit("base", allow_pointless=True, rev_id='A')
333
self.failIf(branch.repository.has_signature_for_revision_id('A'))
450
from ..bzr.testament import Testament
335
from bzrlib.testament import Testament
451
336
# 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'))
337
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
338
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
339
allow_pointless=True,
342
self.assertEqual(Testament.from_revision(branch.repository,
343
'B').as_short_text(),
344
branch.repository.get_signature_text('B'))
467
breezy.gpg.GPGStrategy = oldstrategy
346
bzrlib.gpg.GPGStrategy = oldstrategy
469
348
def test_commit_failed_signature(self):
471
import breezy.commit as commit
472
oldstrategy = breezy.gpg.GPGStrategy
350
import bzrlib.commit as commit
351
oldstrategy = bzrlib.gpg.GPGStrategy
473
352
wt = self.make_branch_and_tree('.')
474
353
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'))
354
wt.commit("base", allow_pointless=True, rev_id='A')
355
self.failIf(branch.repository.has_signature_for_revision_id('A'))
357
from bzrlib.testament import Testament
478
358
# 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,
359
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
360
config = MustSignConfig(branch)
361
self.assertRaises(SigningFailed,
362
commit.Commit(config=config).commit,
486
364
allow_pointless=True,
489
367
branch = Branch.open(self.get_url('.'))
490
self.assertEqual(branch.last_revision(), b'A')
491
self.assertFalse(branch.repository.has_revision(b'B'))
368
self.assertEqual(branch.revision_history(), ['A'])
369
self.failIf(branch.repository.has_revision('B'))
493
breezy.gpg.GPGStrategy = oldstrategy
371
bzrlib.gpg.GPGStrategy = oldstrategy
495
373
def test_commit_invokes_hooks(self):
496
import breezy.commit as commit
374
import bzrlib.commit as commit
497
375
wt = self.make_branch_and_tree('.')
498
376
branch = wt.branch
501
378
def called(branch, rev_id):
502
379
calls.append('called')
503
breezy.ahook = called
380
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',
382
config = BranchWithHooks(branch)
383
commit.Commit(config=config).commit(
385
allow_pointless=True,
386
rev_id='A', working_tree = wt)
509
387
self.assertEqual(['called', 'called'], calls)
513
391
def test_commit_object_doesnt_set_nick(self):
514
392
# using the Commit object directly does not set the branch nick.
515
393
wt = self.make_branch_and_tree('.')
517
395
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
518
self.assertEqual(wt.branch.revno(), 1)
396
self.assertEquals(wt.branch.revno(), 1)
519
397
self.assertEqual({},
520
398
wt.branch.repository.get_revision(
521
wt.branch.last_revision()).properties)
399
wt.branch.last_revision()).properties)
523
401
def test_safe_master_lock(self):
524
402
os.mkdir('master')
604
479
other_tree.rename_one('dirtorename', 'renameddir')
605
480
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
606
481
other_tree.rename_one('filetorename', 'renamedfile')
607
other_tree.rename_one(
608
'filetoreparent', 'renameddir/reparentedfile')
482
other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
609
483
other_tree.remove(['dirtoremove', 'filetoremove'])
610
484
self.build_tree_contents([
611
485
('other/newdir/', ),
612
('other/filetomodify', b'new content'),
613
('other/newfile', b'new file content')])
486
('other/filetomodify', 'new content'),
487
('other/newfile', 'new file content')])
614
488
other_tree.add('newfile')
615
489
other_tree.add('newdir/')
616
490
other_tree.commit('modify all sample files and dirs.')
618
492
other_tree.unlock()
619
this_tree.merge_from_branch(other_tree.branch)
493
self.merge(other_tree.branch, this_tree)
620
494
reporter = CapturingReporter()
621
495
this_tree.commit('do the commit', reporter=reporter)
497
('change', 'unchanged', 'dirtoleave'),
498
('change', 'unchanged', 'filetoleave'),
623
499
('change', 'modified', 'filetomodify'),
624
500
('change', 'added', 'newdir'),
625
501
('change', 'added', 'newfile'),
626
502
('renamed', 'renamed', 'dirtorename', 'renameddir'),
627
('renamed', 'renamed', 'filetorename', 'renamedfile'),
628
503
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
629
504
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
505
('renamed', 'renamed', 'filetorename', 'renamedfile'),
630
506
('deleted', 'dirtoremove'),
631
507
('deleted', 'filetoremove'),
633
result = set(reporter.calls)
634
missing = expected - result
635
new = result - expected
636
self.assertEqual((set(), set()), (missing, new))
638
511
def test_commit_removals_respects_filespec(self):
639
512
"""Commit respects the specified_files for removals."""
664
537
tree = self.make_branch_and_tree('.')
665
538
self.build_tree(['a'])
667
tree.commit('added a', rev_id=b'a1')
540
tree.commit('added a', rev_id='a1')
669
rev = tree.branch.repository.get_revision(b'a1')
542
rev = tree.branch.repository.get_revision('a1')
670
543
timestamp = rev.timestamp
671
544
timestamp_1ms = round(timestamp, 3)
672
545
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'])))