89
51
"""Commit and check two versions of a single file."""
90
52
wt = self.make_branch_and_tree('.')
92
with open('hello', 'w') as f:
93
f.write('hello world')
54
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
56
wt.commit(message='add hello')
57
file_id = wt.path2id('hello')
59
file('hello', 'w').write('version 2')
60
wt.commit(message='commit 2')
62
eq = self.assertEquals
103
rev = b.repository.get_revision(rev1)
64
rh = b.revision_history()
65
rev = b.repository.get_revision(rh[0])
104
66
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'])
68
tree1 = b.repository.revision_tree(rh[0])
69
text = tree1.get_file_text(file_id)
70
eq(text, 'hello world')
72
tree2 = b.repository.revision_tree(rh[1])
73
eq(tree2.get_file_text(file_id), 'version 2')
75
def test_delete_commit(self):
76
"""Test a commit with a deleted file"""
77
wt = self.make_branch_and_tree('.')
79
file('hello', 'w').write('hello world')
80
wt.add(['hello'], ['hello-id'])
166
81
wt.commit(message='add hello')
168
83
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"])
84
wt.commit('removed hello', rev_id='rev2')
86
tree = b.repository.revision_tree('rev2')
87
self.assertFalse(tree.has_id('hello-id'))
198
89
def test_pointless_commit(self):
199
90
"""Commit refuses unless there are changes or it's forced."""
200
91
wt = self.make_branch_and_tree('.')
202
with open('hello', 'w') as f:
93
file('hello', 'w').write('hello')
205
95
wt.commit(message='add hello')
206
self.assertEqual(b.revno(), 1)
96
self.assertEquals(b.revno(), 1)
207
97
self.assertRaises(PointlessCommit,
210
100
allow_pointless=False)
211
self.assertEqual(b.revno(), 1)
101
self.assertEquals(b.revno(), 1)
213
103
def test_commit_empty(self):
214
104
"""Commiting an empty tree works."""
215
105
wt = self.make_branch_and_tree('.')
220
110
message='empty tree',
221
111
allow_pointless=False)
222
112
wt.commit(message='empty tree', allow_pointless=True)
223
self.assertEqual(b.revno(), 2)
113
self.assertEquals(b.revno(), 2)
225
115
def test_selective_delete(self):
226
116
"""Selective commit in tree with deletions"""
227
117
wt = self.make_branch_and_tree('.')
229
with open('hello', 'w') as f:
231
with open('buongia', 'w') as f:
119
file('hello', 'w').write('hello')
120
file('buongia', 'w').write('buongia')
233
121
wt.add(['hello', 'buongia'],
234
[b'hello-id', b'buongia-id'])
122
['hello-id', 'buongia-id'])
235
123
wt.commit(message='add files',
236
rev_id=b'test@rev-1')
238
126
os.remove('hello')
239
with open('buongia', 'w') as f:
127
file('buongia', 'w').write('new text')
241
128
wt.commit(message='update text',
242
specific_files=['buongia'],
243
allow_pointless=False,
244
rev_id=b'test@rev-2')
129
specific_files=['buongia'],
130
allow_pointless=False,
246
133
wt.commit(message='remove hello',
247
specific_files=['hello'],
248
allow_pointless=False,
249
rev_id=b'test@rev-3')
134
specific_files=['hello'],
135
allow_pointless=False,
251
eq = self.assertEqual
138
eq = self.assertEquals
254
tree2 = b.repository.revision_tree(b'test@rev-2')
256
self.addCleanup(tree2.unlock)
141
tree2 = b.repository.revision_tree('test@rev-2')
257
142
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)
143
self.assertEquals(tree2.get_file_text('hello-id'), 'hello')
144
self.assertEquals(tree2.get_file_text('buongia-id'), 'new text')
146
tree3 = b.repository.revision_tree('test@rev-3')
264
147
self.assertFalse(tree3.has_filename('hello'))
265
self.assertEqual(tree3.get_file_text('buongia'), b'new text')
148
self.assertEquals(tree3.get_file_text('buongia-id'), 'new text')
267
150
def test_commit_rename(self):
268
151
"""Test commit of a revision where a file is renamed."""
269
152
tree = self.make_branch_and_tree('.')
271
154
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)
155
tree.add(['hello'], ['hello-id'])
156
tree.commit(message='one', rev_id='test@rev-1', allow_pointless=False)
275
158
tree.rename_one('hello', 'fruity')
276
tree.commit(message='renamed', rev_id=b'test@rev-2',
277
allow_pointless=False)
159
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')
161
eq = self.assertEquals
162
tree1 = b.repository.revision_tree('test@rev-1')
163
eq(tree1.id2path('hello-id'), 'hello')
164
eq(tree1.get_file_text('hello-id'), 'contents of hello\n')
285
165
self.assertFalse(tree1.has_filename('fruity'))
286
self.check_tree_shape(tree1, ['hello'])
287
eq(tree1.get_file_revision('hello'), b'test@rev-1')
166
self.check_inventory_shape(tree1.inventory, ['hello'])
167
ie = tree1.inventory['hello-id']
168
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')
170
tree2 = b.repository.revision_tree('test@rev-2')
171
eq(tree2.id2path('hello-id'), 'fruity')
172
eq(tree2.get_file_text('hello-id'), 'contents of hello\n')
173
self.check_inventory_shape(tree2.inventory, ['fruity'])
174
ie = tree2.inventory['hello-id']
175
eq(ie.revision, 'test@rev-2')
297
177
def test_reused_rev_id(self):
298
178
"""Test that a revision id cannot be reused in a branch"""
299
179
wt = self.make_branch_and_tree('.')
301
wt.commit('initial', rev_id=b'test@rev-1', allow_pointless=True)
181
wt.commit('initial', rev_id='test@rev-1', allow_pointless=True)
302
182
self.assertRaises(Exception,
304
184
message='reused id',
305
rev_id=b'test@rev-1',
306
186
allow_pointless=True)
308
188
def test_commit_move(self):
309
189
"""Test commit of revisions with moved files and directories"""
310
eq = self.assertEqual
190
eq = self.assertEquals
311
191
wt = self.make_branch_and_tree('.')
314
194
self.build_tree(['hello', 'a/', 'b/'])
315
wt.add(['hello', 'a', 'b'], [b'hello-id', b'a-id', b'b-id'])
195
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
316
196
wt.commit('initial', rev_id=r1, allow_pointless=False)
317
197
wt.move(['hello'], 'a')
319
199
wt.commit('two', rev_id=r2, allow_pointless=False)
322
self.check_tree_shape(wt, ['a/', 'a/hello', 'b/'])
200
self.check_inventory_shape(wt.read_working_inventory(),
201
['a', 'a/hello', 'b'])
326
203
wt.move(['b'], 'a')
328
205
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/'])
206
self.check_inventory_shape(wt.read_working_inventory(),
207
['a', 'a/hello', 'a/b'])
208
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
209
['a', 'a/hello', 'a/b'])
338
211
wt.move(['a/hello'], 'a/b')
340
213
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)
214
self.check_inventory_shape(wt.read_working_inventory(),
215
['a', 'a/b/hello', 'a/b'])
217
inv = b.repository.get_revision_inventory(r4)
218
eq(inv['hello-id'].revision, r4)
219
eq(inv['a-id'].revision, r1)
220
eq(inv['b-id'].revision, r3)
352
222
def test_removed_commit(self):
353
223
"""Commit with a removed file"""
354
224
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'])
226
file('hello', 'w').write('hello world')
227
wt.add(['hello'], ['hello-id'])
359
228
wt.commit(message='add hello')
360
229
wt.remove('hello')
361
wt.commit('removed hello', rev_id=b'rev2')
230
wt.commit('removed hello', rev_id='rev2')
363
tree = b.repository.revision_tree(b'rev2')
364
self.assertFalse(tree.has_filename('hello'))
232
tree = b.repository.revision_tree('rev2')
233
self.assertFalse(tree.has_id('hello-id'))
366
235
def test_committed_ancestry(self):
367
236
"""Test commit appends revisions to ancestry."""
371
240
for i in range(4):
372
with open('hello', 'w') as f:
373
f.write((str(i) * 4) + '\n')
241
file('hello', 'w').write((str(i) * 4) + '\n')
375
wt.add(['hello'], [b'hello-id'])
376
rev_id = b'test@rev-%d' % (i + 1)
243
wt.add(['hello'], ['hello-id'])
244
rev_id = 'test@rev-%d' % (i+1)
377
245
rev_ids.append(rev_id)
378
wt.commit(message='rev %d' % (i + 1),
246
wt.commit(message='rev %d' % (i+1),
248
eq = self.assertEquals
249
eq(b.revision_history(), rev_ids)
380
250
for i in range(4):
381
self.assertThat(rev_ids[:i + 1],
382
MatchesAncestry(b.repository, rev_ids[i]))
251
anc = b.repository.get_ancestry(rev_ids[i])
252
eq(anc, [None] + rev_ids[:i+1])
384
254
def test_commit_new_subdir_child_selective(self):
385
255
wt = self.make_branch_and_tree('.')
387
257
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
388
258
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)
259
['dirid', 'file1id', 'file2id'])
260
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
261
inv = b.repository.get_inventory('1')
262
self.assertEqual('1', inv['dirid'].revision)
263
self.assertEqual('1', inv['file1id'].revision)
394
264
# FIXME: This should raise a KeyError I think, rbc20051006
395
self.assertRaises(BzrError, inv.get_entry, b'file2id')
265
self.assertRaises(BzrError, inv.__getitem__, 'file2id')
397
267
def test_strict_commit(self):
398
268
"""Try and commit with unknown files and strict = True, should fail."""
399
from ..errors import StrictCommitFailed
269
from bzrlib.errors import StrictCommitFailed
400
270
wt = self.make_branch_and_tree('.')
402
with open('hello', 'w') as f:
403
f.write('hello world')
272
file('hello', 'w').write('hello world')
405
with open('goodbye', 'w') as f:
406
f.write('goodbye cruel world!')
274
file('goodbye', 'w').write('goodbye cruel world!')
407
275
self.assertRaises(StrictCommitFailed, wt.commit,
408
message='add hello but not goodbye', strict=True)
276
message='add hello but not goodbye', strict=True)
410
278
def test_strict_commit_without_unknowns(self):
411
279
"""Try and commit with no unknown files and strict = True,
281
from bzrlib.errors import StrictCommitFailed
413
282
wt = self.make_branch_and_tree('.')
415
with open('hello', 'w') as f:
416
f.write('hello world')
284
file('hello', 'w').write('hello world')
418
286
wt.commit(message='add hello', strict=True)
434
300
wt = self.make_branch_and_tree('.')
436
with open('hello', 'w') as f:
437
f.write('hello world')
302
file('hello', 'w').write('hello world')
439
304
wt.commit(message='add hello', strict=False)
441
306
def test_signed_commit(self):
443
import breezy.commit as commit
444
oldstrategy = breezy.gpg.GPGStrategy
308
import bzrlib.commit as commit
309
oldstrategy = bzrlib.gpg.GPGStrategy
445
310
wt = self.make_branch_and_tree('.')
446
311
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'))
312
wt.commit("base", allow_pointless=True, rev_id='A')
313
self.failIf(branch.repository.has_signature_for_revision_id('A'))
450
from ..bzr.testament import Testament
315
from bzrlib.testament import Testament
451
316
# 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'))
317
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
318
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
319
allow_pointless=True,
322
self.assertEqual(Testament.from_revision(branch.repository,
323
'B').as_short_text(),
324
branch.repository.get_signature_text('B'))
467
breezy.gpg.GPGStrategy = oldstrategy
326
bzrlib.gpg.GPGStrategy = oldstrategy
469
328
def test_commit_failed_signature(self):
471
import breezy.commit as commit
472
oldstrategy = breezy.gpg.GPGStrategy
330
import bzrlib.commit as commit
331
oldstrategy = bzrlib.gpg.GPGStrategy
473
332
wt = self.make_branch_and_tree('.')
474
333
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'))
334
wt.commit("base", allow_pointless=True, rev_id='A')
335
self.failIf(branch.repository.has_signature_for_revision_id('A'))
337
from bzrlib.testament import Testament
478
338
# 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,
339
bzrlib.gpg.GPGStrategy = bzrlib.gpg.DisabledGPGStrategy
340
config = MustSignConfig(branch)
341
self.assertRaises(SigningFailed,
342
commit.Commit(config=config).commit,
486
344
allow_pointless=True,
489
347
branch = Branch.open(self.get_url('.'))
490
self.assertEqual(branch.last_revision(), b'A')
491
self.assertFalse(branch.repository.has_revision(b'B'))
348
self.assertEqual(branch.revision_history(), ['A'])
349
self.failIf(branch.repository.has_revision('B'))
493
breezy.gpg.GPGStrategy = oldstrategy
351
bzrlib.gpg.GPGStrategy = oldstrategy
495
353
def test_commit_invokes_hooks(self):
496
import breezy.commit as commit
354
import bzrlib.commit as commit
497
355
wt = self.make_branch_and_tree('.')
498
356
branch = wt.branch
501
358
def called(branch, rev_id):
502
359
calls.append('called')
503
breezy.ahook = called
360
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',
362
config = BranchWithHooks(branch)
363
commit.Commit(config=config).commit(
365
allow_pointless=True,
366
rev_id='A', working_tree = wt)
509
367
self.assertEqual(['called', 'called'], calls)
513
371
def test_commit_object_doesnt_set_nick(self):
514
372
# using the Commit object directly does not set the branch nick.
515
373
wt = self.make_branch_and_tree('.')
517
375
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
518
self.assertEqual(wt.branch.revno(), 1)
376
self.assertEquals(wt.branch.revno(), 1)
519
377
self.assertEqual({},
520
378
wt.branch.repository.get_revision(
521
wt.branch.last_revision()).properties)
379
wt.branch.last_revision()).properties)
523
381
def test_safe_master_lock(self):
524
382
os.mkdir('master')
534
392
self.assertRaises(LockContention, wt.commit, 'silly')
536
394
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'])))