45
43
return "bzrlib.ahook bzrlib.ahook"
48
class CapturingReporter(NullCommitReporter):
49
"""This reporter captures the calls made to it for evaluation later."""
52
# a list of the calls this received
55
def snapshot_change(self, change, path):
56
self.calls.append(('change', change, path))
58
def deleted(self, file_id):
59
self.calls.append(('deleted', file_id))
61
def missing(self, path):
62
self.calls.append(('missing', path))
64
def renamed(self, change, old_path, new_path):
65
self.calls.append(('renamed', change, old_path, new_path))
68
class TestCommit(TestCaseWithTransport):
46
class TestCommit(TestCaseInTempDir):
70
48
def test_simple_commit(self):
71
49
"""Commit and check two versions of a single file."""
72
wt = self.make_branch_and_tree('.')
50
b = Branch.initialize(u'.')
74
51
file('hello', 'w').write('hello world')
76
wt.commit(message='add hello')
77
file_id = wt.path2id('hello')
52
b.working_tree().add('hello')
53
b.working_tree().commit(message='add hello')
54
file_id = b.working_tree().path2id('hello')
79
56
file('hello', 'w').write('version 2')
80
wt.commit(message='commit 2')
57
b.working_tree().commit(message='commit 2')
82
59
eq = self.assertEquals
84
61
rh = b.revision_history()
85
rev = b.repository.get_revision(rh[0])
62
rev = b.get_revision(rh[0])
86
63
eq(rev.message, 'add hello')
88
tree1 = b.repository.revision_tree(rh[0])
65
tree1 = b.revision_tree(rh[0])
89
66
text = tree1.get_file_text(file_id)
90
67
eq(text, 'hello world')
92
tree2 = b.repository.revision_tree(rh[1])
69
tree2 = b.revision_tree(rh[1])
93
70
eq(tree2.get_file_text(file_id), 'version 2')
95
72
def test_delete_commit(self):
96
73
"""Test a commit with a deleted file"""
97
wt = self.make_branch_and_tree('.')
74
b = Branch.initialize(u'.')
99
75
file('hello', 'w').write('hello world')
100
wt.add(['hello'], ['hello-id'])
101
wt.commit(message='add hello')
76
b.working_tree().add(['hello'], ['hello-id'])
77
b.working_tree().commit(message='add hello')
103
79
os.remove('hello')
104
wt.commit('removed hello', rev_id='rev2')
80
b.working_tree().commit('removed hello', rev_id='rev2')
106
tree = b.repository.revision_tree('rev2')
82
tree = b.revision_tree('rev2')
107
83
self.assertFalse(tree.has_id('hello-id'))
109
85
def test_pointless_commit(self):
110
86
"""Commit refuses unless there are changes or it's forced."""
111
wt = self.make_branch_and_tree('.')
87
b = Branch.initialize(u'.')
113
88
file('hello', 'w').write('hello')
115
wt.commit(message='add hello')
89
b.working_tree().add(['hello'])
90
b.working_tree().commit(message='add hello')
116
91
self.assertEquals(b.revno(), 1)
117
92
self.assertRaises(PointlessCommit,
93
b.working_tree().commit,
120
95
allow_pointless=False)
121
96
self.assertEquals(b.revno(), 1)
123
98
def test_commit_empty(self):
124
99
"""Commiting an empty tree works."""
125
wt = self.make_branch_and_tree('.')
127
wt.commit(message='empty tree', allow_pointless=True)
100
b = Branch.initialize(u'.')
101
b.working_tree().commit(message='empty tree', allow_pointless=True)
128
102
self.assertRaises(PointlessCommit,
103
b.working_tree().commit,
130
104
message='empty tree',
131
105
allow_pointless=False)
132
wt.commit(message='empty tree', allow_pointless=True)
106
b.working_tree().commit(message='empty tree', allow_pointless=True)
133
107
self.assertEquals(b.revno(), 2)
135
110
def test_selective_delete(self):
136
111
"""Selective commit in tree with deletions"""
137
wt = self.make_branch_and_tree('.')
112
b = Branch.initialize(u'.')
139
113
file('hello', 'w').write('hello')
140
114
file('buongia', 'w').write('buongia')
141
wt.add(['hello', 'buongia'],
115
b.working_tree().add(['hello', 'buongia'],
142
116
['hello-id', 'buongia-id'])
143
wt.commit(message='add files',
117
b.working_tree().commit(message='add files',
144
118
rev_id='test@rev-1')
146
120
os.remove('hello')
147
121
file('buongia', 'w').write('new text')
148
wt.commit(message='update text',
122
b.working_tree().commit(message='update text',
149
123
specific_files=['buongia'],
150
124
allow_pointless=False,
151
125
rev_id='test@rev-2')
153
wt.commit(message='remove hello',
127
b.working_tree().commit(message='remove hello',
154
128
specific_files=['hello'],
155
129
allow_pointless=False,
156
130
rev_id='test@rev-3')
208
182
def test_commit_move(self):
209
183
"""Test commit of revisions with moved files and directories"""
210
184
eq = self.assertEquals
211
wt = self.make_branch_and_tree('.')
185
b = Branch.initialize(u'.')
213
186
r1 = 'test@rev-1'
214
187
self.build_tree(['hello', 'a/', 'b/'])
215
wt.add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
216
wt.commit('initial', rev_id=r1, allow_pointless=False)
217
wt.move(['hello'], 'a')
188
b.working_tree().add(['hello', 'a', 'b'], ['hello-id', 'a-id', 'b-id'])
189
b.working_tree().commit('initial', rev_id=r1, allow_pointless=False)
190
b.working_tree().move(['hello'], 'a')
218
191
r2 = 'test@rev-2'
219
wt.commit('two', rev_id=r2, allow_pointless=False)
220
self.check_inventory_shape(wt.read_working_inventory(),
192
b.working_tree().commit('two', rev_id=r2, allow_pointless=False)
193
self.check_inventory_shape(b.working_tree().read_working_inventory(),
221
194
['a', 'a/hello', 'b'])
196
b.working_tree().move(['b'], 'a')
224
197
r3 = 'test@rev-3'
225
wt.commit('three', rev_id=r3, allow_pointless=False)
226
self.check_inventory_shape(wt.read_working_inventory(),
198
b.working_tree().commit('three', rev_id=r3, allow_pointless=False)
199
self.check_inventory_shape(b.working_tree().read_working_inventory(),
227
200
['a', 'a/hello', 'a/b'])
228
self.check_inventory_shape(b.repository.get_revision_inventory(r3),
201
self.check_inventory_shape(b.get_revision_inventory(r3),
229
202
['a', 'a/hello', 'a/b'])
231
wt.move(['a/hello'], 'a/b')
204
b.working_tree().move(['a/hello'], 'a/b')
232
205
r4 = 'test@rev-4'
233
wt.commit('four', rev_id=r4, allow_pointless=False)
234
self.check_inventory_shape(wt.read_working_inventory(),
206
b.working_tree().commit('four', rev_id=r4, allow_pointless=False)
207
self.check_inventory_shape(b.working_tree().read_working_inventory(),
235
208
['a', 'a/b/hello', 'a/b'])
237
inv = b.repository.get_revision_inventory(r4)
210
inv = b.get_revision_inventory(r4)
238
211
eq(inv['hello-id'].revision, r4)
239
212
eq(inv['a-id'].revision, r1)
240
213
eq(inv['b-id'].revision, r3)
242
215
def test_removed_commit(self):
243
216
"""Commit with a removed file"""
244
wt = self.make_branch_and_tree('.')
217
b = Branch.initialize(u'.')
218
wt = b.working_tree()
246
219
file('hello', 'w').write('hello world')
247
wt.add(['hello'], ['hello-id'])
248
wt.commit(message='add hello')
220
b.working_tree().add(['hello'], ['hello-id'])
221
b.working_tree().commit(message='add hello')
223
wt = b.working_tree() # FIXME: kludge for aliasing of working inventory
249
224
wt.remove('hello')
250
wt.commit('removed hello', rev_id='rev2')
225
b.working_tree().commit('removed hello', rev_id='rev2')
252
tree = b.repository.revision_tree('rev2')
227
tree = b.revision_tree('rev2')
253
228
self.assertFalse(tree.has_id('hello-id'))
255
231
def test_committed_ancestry(self):
256
232
"""Test commit appends revisions to ancestry."""
257
wt = self.make_branch_and_tree('.')
233
b = Branch.initialize(u'.')
260
235
for i in range(4):
261
236
file('hello', 'w').write((str(i) * 4) + '\n')
263
wt.add(['hello'], ['hello-id'])
238
b.working_tree().add(['hello'], ['hello-id'])
264
239
rev_id = 'test@rev-%d' % (i+1)
265
240
rev_ids.append(rev_id)
266
wt.commit(message='rev %d' % (i+1),
241
b.working_tree().commit(message='rev %d' % (i+1),
268
243
eq = self.assertEquals
269
244
eq(b.revision_history(), rev_ids)
270
245
for i in range(4):
271
anc = b.repository.get_ancestry(rev_ids[i])
246
anc = b.get_ancestry(rev_ids[i])
272
247
eq(anc, [None] + rev_ids[:i+1])
274
249
def test_commit_new_subdir_child_selective(self):
275
wt = self.make_branch_and_tree('.')
250
b = Branch.initialize(u'.')
277
251
self.build_tree(['dir/', 'dir/file1', 'dir/file2'])
278
wt.add(['dir', 'dir/file1', 'dir/file2'],
252
b.working_tree().add(['dir', 'dir/file1', 'dir/file2'],
279
253
['dirid', 'file1id', 'file2id'])
280
wt.commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
281
inv = b.repository.get_inventory('1')
254
b.working_tree().commit('dir/file1', specific_files=['dir/file1'], rev_id='1')
255
inv = b.get_inventory('1')
282
256
self.assertEqual('1', inv['dirid'].revision)
283
257
self.assertEqual('1', inv['file1id'].revision)
284
258
# FIXME: This should raise a KeyError I think, rbc20051006
287
261
def test_strict_commit(self):
288
262
"""Try and commit with unknown files and strict = True, should fail."""
289
263
from bzrlib.errors import StrictCommitFailed
290
wt = self.make_branch_and_tree('.')
264
b = Branch.initialize(u'.')
292
265
file('hello', 'w').write('hello world')
266
b.working_tree().add('hello')
294
267
file('goodbye', 'w').write('goodbye cruel world!')
295
self.assertRaises(StrictCommitFailed, wt.commit,
268
self.assertRaises(StrictCommitFailed, b.working_tree().commit,
296
269
message='add hello but not goodbye', strict=True)
298
271
def test_strict_commit_without_unknowns(self):
299
272
"""Try and commit with no unknown files and strict = True,
301
274
from bzrlib.errors import StrictCommitFailed
302
wt = self.make_branch_and_tree('.')
275
b = Branch.initialize(u'.')
304
276
file('hello', 'w').write('hello world')
306
wt.commit(message='add hello', strict=True)
277
b.working_tree().add('hello')
278
b.working_tree().commit(message='add hello', strict=True)
308
280
def test_nonstrict_commit(self):
309
281
"""Try and commit with unknown files and strict = False, should work."""
310
wt = self.make_branch_and_tree('.')
282
b = Branch.initialize(u'.')
312
283
file('hello', 'w').write('hello world')
284
b.working_tree().add('hello')
314
285
file('goodbye', 'w').write('goodbye cruel world!')
315
wt.commit(message='add hello but not goodbye', strict=False)
286
b.working_tree().commit(message='add hello but not goodbye', strict=False)
317
288
def test_nonstrict_commit_without_unknowns(self):
318
289
"""Try and commit with no unknown files and strict = False,
320
wt = self.make_branch_and_tree('.')
291
b = Branch.initialize(u'.')
322
292
file('hello', 'w').write('hello world')
324
wt.commit(message='add hello', strict=False)
293
b.working_tree().add('hello')
294
b.working_tree().commit(message='add hello', strict=False)
326
296
def test_signed_commit(self):
327
297
import bzrlib.gpg
328
298
import bzrlib.commit as commit
329
299
oldstrategy = bzrlib.gpg.GPGStrategy
330
wt = self.make_branch_and_tree('.')
332
wt.commit("base", allow_pointless=True, rev_id='A')
333
self.failIf(branch.repository.has_signature_for_revision_id('A'))
300
branch = Branch.initialize(u'.')
301
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
302
self.failIf(branch.revision_store.has_id('A', 'sig'))
335
304
from bzrlib.testament import Testament
336
305
# monkey patch gpg signing mechanism
337
306
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
338
commit.Commit(config=MustSignConfig(branch)).commit(message="base",
307
commit.Commit(config=MustSignConfig(branch)).commit(branch, "base",
339
308
allow_pointless=True,
342
self.assertEqual(Testament.from_revision(branch.repository,
343
'B').as_short_text(),
344
branch.repository.get_signature_text('B'))
310
self.assertEqual(Testament.from_revision(branch,'B').as_short_text(),
311
branch.revision_store.get('B', 'sig').read())
346
313
bzrlib.gpg.GPGStrategy = oldstrategy
382
346
config = BranchWithHooks(branch)
383
347
commit.Commit(config=config).commit(
385
349
allow_pointless=True,
386
rev_id='A', working_tree = wt)
387
351
self.assertEqual(['called', 'called'], calls)
391
def test_commit_object_doesnt_set_nick(self):
392
# using the Commit object directly does not set the branch nick.
393
wt = self.make_branch_and_tree('.')
395
c.commit(working_tree=wt, message='empty tree', allow_pointless=True)
396
self.assertEquals(wt.branch.revno(), 1)
398
wt.branch.repository.get_revision(
399
wt.branch.last_revision()).properties)
401
def test_safe_master_lock(self):
403
master = BzrDirMetaFormat1().initialize('master')
404
master.create_repository()
405
master_branch = master.create_branch()
406
master.create_workingtree()
407
bound = master.sprout('bound')
408
wt = bound.open_workingtree()
409
wt.branch.set_bound_location(os.path.realpath('master'))
410
master_branch.lock_write()
412
self.assertRaises(LockContention, wt.commit, 'silly')
414
master_branch.unlock()
416
def test_commit_bound_merge(self):
417
# see bug #43959; commit of a merge in a bound branch fails to push
418
# the new commit into the master
419
master_branch = self.make_branch('master')
420
bound_tree = self.make_branch_and_tree('bound')
421
bound_tree.branch.bind(master_branch)
423
self.build_tree_contents([('bound/content_file', 'initial contents\n')])
424
bound_tree.add(['content_file'])
425
bound_tree.commit(message='woo!')
427
other_bzrdir = master_branch.bzrdir.sprout('other')
428
other_tree = other_bzrdir.open_workingtree()
430
# do a commit to the the other branch changing the content file so
431
# that our commit after merging will have a merged revision in the
432
# content file history.
433
self.build_tree_contents([('other/content_file', 'change in other\n')])
434
other_tree.commit('change in other')
436
# do a merge into the bound branch from other, and then change the
437
# content file locally to force a new revision (rather than using the
438
# revision from other). This forces extra processing in commit.
439
self.merge(other_tree.branch, bound_tree)
440
self.build_tree_contents([('bound/content_file', 'change in bound\n')])
442
# before #34959 was fixed, this failed with 'revision not present in
443
# weave' when trying to implicitly push from the bound branch to the master
444
bound_tree.commit(message='commit of merge in bound tree')
446
def test_commit_reporting_after_merge(self):
447
# when doing a commit of a merge, the reporter needs to still
448
# be called for each item that is added/removed/deleted.
449
this_tree = self.make_branch_and_tree('this')
450
# we need a bunch of files and dirs, to perform one action on each.
453
'this/dirtoreparent/',
456
'this/filetoreparent',
473
this_tree.commit('create_files')
474
other_dir = this_tree.bzrdir.sprout('other')
475
other_tree = other_dir.open_workingtree()
476
other_tree.lock_write()
477
# perform the needed actions on the files and dirs.
479
other_tree.rename_one('dirtorename', 'renameddir')
480
other_tree.rename_one('dirtoreparent', 'renameddir/reparenteddir')
481
other_tree.rename_one('filetorename', 'renamedfile')
482
other_tree.rename_one('filetoreparent', 'renameddir/reparentedfile')
483
other_tree.remove(['dirtoremove', 'filetoremove'])
484
self.build_tree_contents([
486
('other/filetomodify', 'new content'),
487
('other/newfile', 'new file content')])
488
other_tree.add('newfile')
489
other_tree.add('newdir/')
490
other_tree.commit('modify all sample files and dirs.')
493
self.merge(other_tree.branch, this_tree)
494
reporter = CapturingReporter()
495
this_tree.commit('do the commit', reporter=reporter)
497
('change', 'unchanged', ''),
498
('change', 'unchanged', 'dirtoleave'),
499
('change', 'unchanged', 'filetoleave'),
500
('change', 'modified', 'filetomodify'),
501
('change', 'added', 'newdir'),
502
('change', 'added', 'newfile'),
503
('renamed', 'renamed', 'dirtorename', 'renameddir'),
504
('renamed', 'renamed', 'dirtoreparent', 'renameddir/reparenteddir'),
505
('renamed', 'renamed', 'filetoreparent', 'renameddir/reparentedfile'),
506
('renamed', 'renamed', 'filetorename', 'renamedfile'),
507
('deleted', 'dirtoremove'),
508
('deleted', 'filetoremove'),
512
def test_commit_removals_respects_filespec(self):
513
"""Commit respects the specified_files for removals."""
514
tree = self.make_branch_and_tree('.')
515
self.build_tree(['a', 'b'])
517
tree.commit('added a, b')
518
tree.remove(['a', 'b'])
519
tree.commit('removed a', specific_files='a')
520
basis = tree.basis_tree().inventory
521
self.assertIs(None, basis.path2id('a'))
522
self.assertFalse(basis.path2id('b') is None)
524
def test_commit_saves_1ms_timestamp(self):
525
"""Passing in a timestamp is saved with 1ms resolution"""
526
tree = self.make_branch_and_tree('.')
527
self.build_tree(['a'])
529
tree.commit('added a', timestamp=1153248633.4186721, timezone=0,
532
rev = tree.branch.repository.get_revision('a1')
533
self.assertEqual(1153248633.419, rev.timestamp)
535
def test_commit_has_1ms_resolution(self):
536
"""Allowing commit to generate the timestamp also has 1ms resolution"""
537
tree = self.make_branch_and_tree('.')
538
self.build_tree(['a'])
540
tree.commit('added a', rev_id='a1')
542
rev = tree.branch.repository.get_revision('a1')
543
timestamp = rev.timestamp
544
timestamp_1ms = round(timestamp, 3)
545
self.assertEqual(timestamp_1ms, timestamp)