81
86
"""Commit and check two versions of a single file."""
82
87
wt = self.make_branch_and_tree('.')
84
file('hello', 'w').write('hello world')
89
with open('hello', 'w') as f: f.write('hello world')
86
wt.commit(message='add hello')
87
file_id = wt.path2id('hello')
89
file('hello', 'w').write('version 2')
90
wt.commit(message='commit 2')
92
eq = self.assertEquals
91
rev1 = wt.commit(message='add hello')
93
with open('hello', 'w') as f: f.write('version 2')
94
rev2 = wt.commit(message='commit 2')
94
rh = b.revision_history()
95
rev = b.repository.get_revision(rh[0])
98
rev = b.repository.get_revision(rev1)
96
99
eq(rev.message, 'add hello')
98
tree1 = b.repository.revision_tree(rh[0])
101
tree1 = b.repository.revision_tree(rev1)
100
text = tree1.get_file_text(file_id)
103
text = tree1.get_file_text('hello')
102
self.assertEqual('hello world', text)
105
self.assertEqual(b'hello world', text)
104
tree2 = b.repository.revision_tree(rh[1])
107
tree2 = b.repository.revision_tree(rev2)
105
108
tree2.lock_read()
106
text = tree2.get_file_text(file_id)
109
text = tree2.get_file_text('hello')
108
self.assertEqual('version 2', text)
111
self.assertEqual(b'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=b'revid', lossy=True)
120
self.assertEqual('revid', revid)
122
def test_commit_lossy_foreign(self):
123
"""Attempt a lossy commit to a foreign branch."""
124
test_foreign.register_dummy_foreign_for_test(self)
125
wt = self.make_branch_and_tree('.',
126
format=test_foreign.DummyForeignVcsDirFormat())
128
with open('hello', 'w') as f: f.write('hello world')
130
revid = wt.commit(message='add hello', lossy=True,
131
timestamp=1302659388, timezone=0)
132
self.assertEqual('dummy-v1:1302659388.0-0-UNKNOWN', revid)
134
def test_commit_bound_lossy_foreign(self):
135
"""Attempt a lossy commit to a bzr branch bound to a foreign branch."""
136
test_foreign.register_dummy_foreign_for_test(self)
137
foreign_branch = self.make_branch('foreign',
138
format=test_foreign.DummyForeignVcsDirFormat())
139
wt = foreign_branch.create_checkout("local")
141
with open('local/hello', 'w') as f: f.write('hello world')
143
revid = wt.commit(message='add hello', lossy=True,
144
timestamp=1302659388, timezone=0)
145
self.assertEqual('dummy-v1:1302659388.0-0-0', revid)
146
self.assertEqual('dummy-v1:1302659388.0-0-0',
147
foreign_branch.last_revision())
148
self.assertEqual('dummy-v1:1302659388.0-0-0',
149
wt.branch.last_revision())
110
151
def test_missing_commit(self):
111
152
"""Test a commit with a missing file"""
112
153
wt = self.make_branch_and_tree('.')
114
file('hello', 'w').write('hello world')
115
wt.add(['hello'], ['hello-id'])
155
with open('hello', 'w') as f: f.write('hello world')
156
wt.add(['hello'], [b'hello-id'])
116
157
wt.commit(message='add hello')
118
159
os.remove('hello')
119
wt.commit('removed hello', rev_id='rev2')
160
reporter = CapturingReporter()
161
wt.commit('removed hello', rev_id=b'rev2', reporter=reporter)
163
[('missing', u'hello'), ('deleted', u'hello')],
121
tree = b.repository.revision_tree('rev2')
122
self.assertFalse(tree.has_id('hello-id'))
166
tree = b.repository.revision_tree(b'rev2')
167
self.assertFalse(tree.has_id(b'hello-id'))
124
169
def test_partial_commit_move(self):
125
170
"""Test a partial commit where a file was renamed but not committed.
165
210
message='empty tree',
166
211
allow_pointless=False)
167
212
wt.commit(message='empty tree', allow_pointless=True)
168
self.assertEquals(b.revno(), 2)
213
self.assertEqual(b.revno(), 2)
170
215
def test_selective_delete(self):
171
216
"""Selective commit in tree with deletions"""
172
217
wt = self.make_branch_and_tree('.')
174
file('hello', 'w').write('hello')
175
file('buongia', 'w').write('buongia')
219
with open('hello', 'w') as f: f.write('hello')
220
with open('buongia', 'w') as f: f.write('buongia')
176
221
wt.add(['hello', 'buongia'],
177
['hello-id', 'buongia-id'])
222
[b'hello-id', b'buongia-id'])
178
223
wt.commit(message='add files',
224
rev_id=b'test@rev-1')
181
226
os.remove('hello')
182
file('buongia', 'w').write('new text')
227
with open('buongia', 'w') as f: f.write('new text')
183
228
wt.commit(message='update text',
184
229
specific_files=['buongia'],
185
230
allow_pointless=False,
231
rev_id=b'test@rev-2')
188
233
wt.commit(message='remove hello',
189
234
specific_files=['hello'],
190
235
allow_pointless=False,
236
rev_id=b'test@rev-3')
193
eq = self.assertEquals
238
eq = self.assertEqual
196
tree2 = b.repository.revision_tree('test@rev-2')
241
tree2 = b.repository.revision_tree(b'test@rev-2')
197
242
tree2.lock_read()
198
243
self.addCleanup(tree2.unlock)
199
244
self.assertTrue(tree2.has_filename('hello'))
200
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
201
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
245
self.assertEqual(tree2.get_file_text('hello'), b'hello')
246
self.assertEqual(tree2.get_file_text('buongia'), b'new text')
203
tree3 = b.repository.revision_tree('test@rev-3')
248
tree3 = b.repository.revision_tree(b'test@rev-3')
204
249
tree3.lock_read()
205
250
self.addCleanup(tree3.unlock)
206
251
self.assertFalse(tree3.has_filename('hello'))
207
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
252
self.assertEqual(tree3.get_file_text('buongia'), b'new text')
209
254
def test_commit_rename(self):
210
255
"""Test commit of a revision where a file is renamed."""
211
256
tree = self.make_branch_and_tree('.')
213
258
self.build_tree(['hello'], line_endings='binary')
214
tree.add(['hello'], ['hello-id'])
215
tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
259
tree.add(['hello'], [b'hello-id'])
260
tree.commit(message='one', rev_id=b'test@rev-1', allow_pointless=False)
217
262
tree.rename_one('hello', 'fruity')
218
tree.commit(message='renamed', rev_id='test@rev-2', allow_pointless=False)
263
tree.commit(message='renamed', rev_id=b'test@rev-2', allow_pointless=False)
220
eq = self.assertEquals
221
tree1 = b.repository.revision_tree('test@rev-1')
265
eq = self.assertEqual
266
tree1 = b.repository.revision_tree(b'test@rev-1')
222
267
tree1.lock_read()
223
268
self.addCleanup(tree1.unlock)
224
eq(tree1.id2path('hello-id'), 'hello')
225
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
269
eq(tree1.id2path(b'hello-id'), b'hello')
270
eq(tree1.get_file_text('hello'), b'contents of hello\n')
226
271
self.assertFalse(tree1.has_filename('fruity'))
227
self.check_inventory_shape(tree1.inventory, ['hello'])
228
ie = tree1.inventory['hello-id']
229
eq(ie.revision, 'test@rev-1')
272
self.check_tree_shape(tree1, ['hello'])
273
eq(tree1.get_file_revision('hello'), b'test@rev-1')
231
tree2 = b.repository.revision_tree('test@rev-2')
275
tree2 = b.repository.revision_tree(b'test@rev-2')
232
276
tree2.lock_read()
233
277
self.addCleanup(tree2.unlock)
234
eq(tree2.id2path('hello-id'), 'fruity')
235
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
236
self.check_inventory_shape(tree2.inventory, ['fruity'])
237
ie = tree2.inventory['hello-id']
238
eq(ie.revision, 'test@rev-2')
278
eq(tree2.id2path(b'hello-id'), 'fruity')
279
eq(tree2.get_file_text('fruity'), b'contents of hello\n')
280
self.check_tree_shape(tree2, ['fruity'])
281
eq(tree2.get_file_revision('fruity'), b'test@rev-2')
240
283
def test_reused_rev_id(self):
241
284
"""Test that a revision id cannot be reused in a branch"""
242
285
wt = self.make_branch_and_tree('.')
244
wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
287
wt.commit('initial', rev_id=b'test@rev-1', allow_pointless=True)
245
288
self.assertRaises(Exception,
247
290
message='reused id',
291
rev_id=b'test@rev-1',
249
292
allow_pointless=True)
251
294
def test_commit_move(self):
252
295
"""Test commit of revisions with moved files and directories"""
253
eq = self.assertEquals
296
eq = self.assertEqual
254
297
wt = self.make_branch_and_tree('.')
257
300
self.build_tree(['hello', 'a/', 'b/'])
258
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
301
wt.add(['hello', 'a', 'b'], [b'hello-id', b'a-id', b'b-id'])
259
302
wt.commit('initial', rev_id=r1, allow_pointless=False)
260
303
wt.move(['hello'], 'a')
262
305
wt.commit('two', rev_id=r2, allow_pointless=False)
265
self.check_inventory_shape(wt.read_working_inventory(),
266
['a/', 'a/hello', 'b/'])
308
self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
270
312
wt.move(['b'], 'a')
272
314
wt.commit('three', rev_id=r3, allow_pointless=False)
275
self.check_inventory_shape(wt.read_working_inventory(),
317
self.check_tree_shape(wt,
276
318
['a/', 'a/hello', 'a/b/'])
277
self.check_inventory_shape(b.repository.get_inventory(r3),
319
self.check_tree_shape(b.repository.revision_tree(r3),
278
320
['a/', 'a/hello', 'a/b/'])
282
324
wt.move(['a/hello'], 'a/b')
284
326
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/'])
329
self.check_tree_shape(wt, ['a/', 'a/b/hello', 'a/b/'])
292
333
inv = b.repository.get_inventory(r4)
293
eq(inv['hello-id'].revision, r4)
294
eq(inv['a-id'].revision, r1)
295
eq(inv['b-id'].revision, r3)
334
eq(inv.get_entry(b'hello-id').revision, r4)
335
eq(inv.get_entry(b'a-id').revision, r1)
336
eq(inv.get_entry(b'b-id').revision, r3)
297
338
def test_removed_commit(self):
298
339
"""Commit with a removed file"""
299
340
wt = self.make_branch_and_tree('.')
301
file('hello', 'w').write('hello world')
302
wt.add(['hello'], ['hello-id'])
342
with open('hello', 'w') as f: f.write('hello world')
343
wt.add(['hello'], [b'hello-id'])
303
344
wt.commit(message='add hello')
304
345
wt.remove('hello')
305
wt.commit('removed hello', rev_id='rev2')
346
wt.commit('removed hello', rev_id=b'rev2')
307
tree = b.repository.revision_tree('rev2')
308
self.assertFalse(tree.has_id('hello-id'))
348
tree = b.repository.revision_tree(b'rev2')
349
self.assertFalse(tree.has_id(b'hello-id'))
310
351
def test_committed_ancestry(self):
311
352
"""Test commit appends revisions to ancestry."""
315
356
for i in range(4):
316
file('hello', 'w').write((str(i) * 4) + '\n')
357
with open('hello', 'w') as f: f.write((str(i) * 4) + '\n')
318
wt.add(['hello'], ['hello-id'])
359
wt.add(['hello'], [b'hello-id'])
319
360
rev_id = 'test@rev-%d' % (i+1)
320
361
rev_ids.append(rev_id)
321
362
wt.commit(message='rev %d' % (i+1),
323
eq = self.assertEquals
324
eq(b.revision_history(), rev_ids)
325
364
for i in range(4):
326
anc = b.repository.get_ancestry(rev_ids[i])
327
eq(anc, [None] + rev_ids[:i+1])
365
self.assertThat(rev_ids[:i+1],
366
MatchesAncestry(b.repository, rev_ids[i]))
329
368
def test_commit_new_subdir_child_selective(self):
330
369
wt = self.make_branch_and_tree('.')
332
371
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
333
372
wt.add(['dir', 'dir/file1', 'dir/file2'],
334
['dirid', 'file1id', 'file2id'])
335
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
336
inv = b.repository.get_inventory('1')
337
self.assertEqual('1', inv['dirid'].revision)
338
self.assertEqual('1', inv['file1id'].revision)
373
[b'dirid', b'file1id', b'file2id'])
374
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id=b'1')
375
inv = b.repository.get_inventory(b'1')
376
self.assertEqual(b'1', inv.get_entry(b'dirid').revision)
377
self.assertEqual(b'1', inv.get_entry(b'file1id').revision)
339
378
# FIXME: This should raise a KeyError I think, rbc20051006
340
self.assertRaises(BzrError, inv.__getitem__, 'file2id')
379
self.assertRaises(BzrError, inv.get_entry, b'file2id')
342
381
def test_strict_commit(self):
343
382
"""Try and commit with unknown files and strict = True, should fail."""
344
from bzrlib.errors import StrictCommitFailed
383
from ..errors import StrictCommitFailed
345
384
wt = self.make_branch_and_tree('.')
347
file('hello', 'w').write('hello world')
386
with open('hello', 'w') as f: f.write('hello world')
349
file('goodbye', 'w').write('goodbye cruel world!')
388
with open('goodbye', 'w') as f: f.write('goodbye cruel world!')
350
389
self.assertRaises(StrictCommitFailed, wt.commit,
351
390
message='add hello but not goodbye', strict=True)
353
392
def test_strict_commit_without_unknowns(self):
354
393
"""Try and commit with no unknown files and strict = True,
356
from bzrlib.errors import StrictCommitFailed
357
395
wt = self.make_branch_and_tree('.')
359
file('hello', 'w').write('hello world')
397
with open('hello', 'w') as f: f.write('hello world')
361
399
wt.commit(message='add hello', strict=True)
375
413
wt = self.make_branch_and_tree('.')
377
file('hello', 'w').write('hello world')
415
with open('hello', 'w') as f: f.write('hello world')
379
417
wt.commit(message='add hello', strict=False)
381
419
def test_signed_commit(self):
383
import bzrlib.commit as commit
384
oldstrategy = bzrlib.gpg.GPGStrategy
421
import breezy.commit as commit
422
oldstrategy = breezy.gpg.GPGStrategy
385
423
wt = self.make_branch_and_tree('.')
386
424
branch = wt.branch
387
wt.commit("base", allow_pointless=True, rev_id='A')
388
self.failIf(branch.repository.has_signature_for_revision_id('A'))
425
wt.commit("base", allow_pointless=True, rev_id=b'A')
426
self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
390
from bzrlib.testament import Testament
428
from ..testament import Testament
391
429
# monkey patch gpg signing mechanism
392
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
393
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
394
allow_pointless=True,
430
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
431
conf = config.MemoryStack(b'''
432
create_signatures=always
434
commit.Commit(config_stack=conf).commit(
435
message="base", allow_pointless=True, rev_id=b'B',
398
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
438
return breezy.gpg.LoopbackGPGStrategy(None).sign(
439
text, breezy.gpg.MODE_CLEAR)
399
440
self.assertEqual(sign(Testament.from_revision(branch.repository,
400
'B').as_short_text()),
441
'B').as_short_text()),
401
442
branch.repository.get_signature_text('B'))
403
bzrlib.gpg.GPGStrategy = oldstrategy
444
breezy.gpg.GPGStrategy = oldstrategy
405
446
def test_commit_failed_signature(self):
407
import bzrlib.commit as commit
408
oldstrategy = bzrlib.gpg.GPGStrategy
448
import breezy.commit as commit
449
oldstrategy = breezy.gpg.GPGStrategy
409
450
wt = self.make_branch_and_tree('.')
410
451
branch = wt.branch
411
wt.commit("base", allow_pointless=True, rev_id='A')
412
self.failIf(branch.repository.has_signature_for_revision_id('A'))
452
wt.commit("base", allow_pointless=True, rev_id=b'A')
453
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
414
from bzrlib.testament import Testament
415
455
# monkey patch gpg signing mechanism
416
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
417
config = MustSignConfig(branch)
418
self.assertRaises(SigningFailed,
419
commit.Commit(config=config).commit,
456
breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy
457
conf = config.MemoryStack(b'''
458
create_signatures=always
460
self.assertRaises(breezy.gpg.SigningFailed,
461
commit.Commit(config_stack=conf).commit,
421
463
allow_pointless=True,
424
466
branch = Branch.open(self.get_url('.'))
425
self.assertEqual(branch.revision_history(), ['A'])
426
self.failIf(branch.repository.has_revision('B'))
467
self.assertEqual(branch.last_revision(), b'A')
468
self.assertFalse(branch.repository.has_revision(b'B'))
428
bzrlib.gpg.GPGStrategy = oldstrategy
470
breezy.gpg.GPGStrategy = oldstrategy
430
472
def test_commit_invokes_hooks(self):
431
import bzrlib.commit as commit
473
import breezy.commit as commit
432
474
wt = self.make_branch_and_tree('.')
433
475
branch = wt.branch
435
477
def called(branch, rev_id):
436
478
calls.append('called')
437
bzrlib.ahook = called
479
breezy.ahook = called
439
config = BranchWithHooks(branch)
440
commit.Commit(config=config).commit(
442
allow_pointless=True,
443
rev_id='A', working_tree = wt)
481
conf = config.MemoryStack(b'post_commit=breezy.ahook breezy.ahook')
482
commit.Commit(config_stack=conf).commit(
483
message = "base", allow_pointless=True, rev_id=b'A',
444
485
self.assertEqual(['called', 'called'], calls)
448
489
def test_commit_object_doesnt_set_nick(self):
449
490
# using the Commit object directly does not set the branch nick.
450
491
wt = self.make_branch_and_tree('.')
452
493
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
453
self.assertEquals(wt.branch.revno(), 1)
494
self.assertEqual(wt.branch.revno(), 1)
454
495
self.assertEqual({},
455
496
wt.branch.repository.get_revision(
456
497
wt.branch.last_revision()).properties)
617
655
self.requireFeature(SymlinkFeature)
618
656
tree = self.make_branch_and_tree('.')
619
657
os.symlink('target', 'name')
620
tree.add('name', 'a-file-id')
658
tree.add('name', b'a-file-id')
621
659
tree.commit('Added a symlink')
622
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
660
self.assertBasisTreeKind('symlink', tree, 'name')
624
662
os.unlink('name')
625
663
self.build_tree(['name'])
626
664
tree.commit('Changed symlink to file')
627
self.assertBasisTreeKind('file', tree, 'a-file-id')
665
self.assertBasisTreeKind('file', tree, 'name')
629
667
os.unlink('name')
630
668
os.symlink('target', 'name')
631
669
tree.commit('file to symlink')
632
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
670
self.assertBasisTreeKind('symlink', tree, 'name')
634
672
os.unlink('name')
636
674
tree.commit('symlink to directory')
637
self.assertBasisTreeKind('directory', tree, 'a-file-id')
675
self.assertBasisTreeKind('directory', tree, 'name')
640
678
os.symlink('target', 'name')
641
679
tree.commit('directory to symlink')
642
self.assertBasisTreeKind('symlink', tree, 'a-file-id')
680
self.assertBasisTreeKind('symlink', tree, 'name')
644
682
# prepare for directory <-> file tests
645
683
os.unlink('name')
647
685
tree.commit('symlink to directory')
648
self.assertBasisTreeKind('directory', tree, 'a-file-id')
686
self.assertBasisTreeKind('directory', tree, 'name')
651
689
self.build_tree(['name'])
652
690
tree.commit('Changed directory to file')
653
self.assertBasisTreeKind('file', tree, 'a-file-id')
691
self.assertBasisTreeKind('file', tree, 'name')
655
693
os.unlink('name')
657
695
tree.commit('file to directory')
658
self.assertBasisTreeKind('directory', tree, 'a-file-id')
696
self.assertBasisTreeKind('directory', tree, 'name')
660
698
def test_commit_unversioned_specified(self):
661
699
"""Commit should raise if specified files isn't in basis or worktree"""
796
827
def test_commit_with_checkout_and_branch_sharing_repo(self):
797
828
repo = self.make_repository('repo', shared=True)
798
829
# make_branch_and_tree ignores shared repos
799
branch = bzrdir.BzrDir.create_branch_convenience('repo/branch')
830
branch = controldir.ControlDir.create_branch_convenience('repo/branch')
800
831
tree2 = branch.create_checkout('repo/tree2')
801
tree2.commit('message', rev_id='rev1')
802
self.assertTrue(tree2.branch.repository.has_revision('rev1'))
832
tree2.commit('message', rev_id=b'rev1')
833
self.assertTrue(tree2.branch.repository.has_revision(b'rev1'))
836
class FilterExcludedTests(TestCase):
838
def test_add_file_not_excluded(self):
840
('fid', (None, 'newpath'),
841
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
842
('file', 'file'), (True, True))]
843
self.assertEqual(changes, list(filter_excluded(changes, ['otherpath'])))
845
def test_add_file_excluded(self):
847
('fid', (None, 'newpath'),
848
0, (False, False), ('pid', 'pid'), ('newpath', 'newpath'),
849
('file', 'file'), (True, True))]
850
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))
852
def test_delete_file_excluded(self):
854
('fid', ('somepath', None),
855
0, (False, None), ('pid', None), ('newpath', None),
856
('file', None), (True, None))]
857
self.assertEqual([], list(filter_excluded(changes, ['somepath'])))
859
def test_move_from_or_to_excluded(self):
861
('fid', ('oldpath', 'newpath'),
862
0, (False, False), ('pid', 'pid'), ('oldpath', 'newpath'),
863
('file', 'file'), (True, True))]
864
self.assertEqual([], list(filter_excluded(changes, ['oldpath'])))
865
self.assertEqual([], list(filter_excluded(changes, ['newpath'])))