89
92
class TestCommit(TestCaseWithWorkingTree):
94
def test_autodelete_renamed(self):
95
tree_a = self.make_branch_and_tree('a')
96
self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2'])
97
tree_a.add(['dir', 'dir/f1', 'dir/f2'], ['dir-id', 'f1-id', 'f2-id'])
98
rev_id1 = tree_a.commit('init')
99
# Start off by renaming entries,
100
# but then actually auto delete the whole tree
101
# https://bugs.launchpad.net/bzr/+bug/114615
102
tree_a.rename_one('dir/f1', 'dir/a')
103
tree_a.rename_one('dir/f2', 'dir/z')
104
osutils.rmtree('a/dir')
105
tree_a.commit('autoremoved')
109
root_id = tree_a.get_root_id()
110
paths = [(path, ie.file_id)
111
for path, ie in tree_a.iter_entries_by_dir()]
114
# The only paths left should be the root
115
self.assertEqual([('', root_id)], paths)
117
def test_no_autodelete_renamed_away(self):
118
tree_a = self.make_branch_and_tree('a')
119
self.build_tree(['a/dir/', 'a/dir/f1', 'a/dir/f2', 'a/dir2/'])
120
tree_a.add(['dir', 'dir/f1', 'dir/f2', 'dir2'],
121
['dir-id', 'f1-id', 'f2-id', 'dir2-id'])
122
rev_id1 = tree_a.commit('init')
123
# Rename one entry out of this directory
124
tree_a.rename_one('dir/f1', 'dir2/a')
125
osutils.rmtree('a/dir')
126
tree_a.commit('autoremoved')
130
root_id = tree_a.get_root_id()
131
paths = [(path, ie.file_id)
132
for path, ie in tree_a.iter_entries_by_dir()]
135
# The only paths left should be the root
136
self.assertEqual([('', root_id), ('dir2', 'dir2-id'),
140
def test_no_autodelete_alternate_renamed(self):
141
# Test for bug #114615
142
tree_a = self.make_branch_and_tree('A')
143
self.build_tree(['A/a/', 'A/a/m', 'A/a/n'])
144
tree_a.add(['a', 'a/m', 'a/n'], ['a-id', 'm-id', 'n-id'])
145
tree_a.commit('init')
149
root_id = tree_a.get_root_id()
153
tree_b = tree_a.bzrdir.sprout('B').open_workingtree()
154
self.build_tree(['B/xyz/'])
155
tree_b.add(['xyz'], ['xyz-id'])
156
tree_b.rename_one('a/m', 'xyz/m')
157
osutils.rmtree('B/a')
158
tree_b.commit('delete in B')
160
paths = [(path, ie.file_id)
161
for path, ie in tree_b.iter_entries_by_dir()]
162
self.assertEqual([('', root_id),
167
self.build_tree_contents([('A/a/n', 'new contents for n\n')])
168
tree_a.commit('change n in A')
170
# Merging from A should introduce conflicts because 'n' was modified
171
# and removed, so 'a' needs to be restored.
172
num_conflicts = tree_b.merge_from_branch(tree_a.branch)
173
self.assertEqual(3, num_conflicts)
174
paths = [(path, ie.file_id)
175
for path, ie in tree_b.iter_entries_by_dir()]
176
self.assertEqual([('', root_id),
179
('a/n.OTHER', 'n-id'),
182
osutils.rmtree('B/a')
185
tree_b.set_conflicts(conflicts.ConflictList())
186
except errors.UnsupportedOperation:
187
# On WT2, set_conflicts is unsupported, but the rmtree has the same
190
tree_b.commit('autoremove a, without touching xyz/m')
191
paths = [(path, ie.file_id)
192
for path, ie in tree_b.iter_entries_by_dir()]
193
self.assertEqual([('', root_id),
198
def test_commit_exclude_pending_merge_fails(self):
199
"""Excludes are a form of partial commit."""
200
wt = self.make_branch_and_tree('.')
201
self.build_tree(['foo'])
203
wt.commit('commit one')
204
wt2 = wt.bzrdir.sprout('to').open_workingtree()
205
wt2.commit('change_right')
206
wt.merge_from_branch(wt2.branch)
207
self.assertRaises(errors.CannotCommitSelectedFileMerge,
208
wt.commit, 'test', exclude=['foo'])
210
def test_commit_exclude_exclude_changed_is_pointless(self):
211
tree = self.make_branch_and_tree('.')
212
self.build_tree(['a'])
213
tree.smart_add(['.'])
214
tree.commit('setup test')
215
self.build_tree_contents([('a', 'new contents for "a"\n')])
216
self.assertRaises(errors.PointlessCommit, tree.commit, 'test',
217
exclude=['a'], allow_pointless=False)
219
def test_commit_exclude_excludes_modified_files(self):
220
tree = self.make_branch_and_tree('.')
221
self.build_tree(['a', 'b', 'c'])
222
tree.smart_add(['.'])
223
tree.commit('test', exclude=['b', 'c'])
224
# If b was excluded it will still be 'added' in status.
226
self.addCleanup(tree.unlock)
227
changes = list(tree.iter_changes(tree.basis_tree()))
228
self.assertEqual(2, len(changes))
229
self.assertEqual((None, 'b'), changes[0][1])
230
self.assertEqual((None, 'c'), changes[1][1])
232
def test_commit_exclude_subtree_of_selected(self):
233
tree = self.make_branch_and_tree('.')
234
self.build_tree(['a/', 'a/b'])
235
tree.smart_add(['.'])
236
tree.commit('test', specific_files=['a'], exclude=['a/b'])
237
# If a/b was excluded it will still be 'added' in status.
239
self.addCleanup(tree.unlock)
240
changes = list(tree.iter_changes(tree.basis_tree()))
241
self.assertEqual(1, len(changes))
242
self.assertEqual((None, 'a/b'), changes[0][1])
91
244
def test_commit_sets_last_revision(self):
92
245
tree = self.make_branch_and_tree('tree')
93
committed_id = tree.commit('foo', rev_id='foo', allow_pointless=True)
246
committed_id = tree.commit('foo', rev_id='foo')
94
247
self.assertEqual(['foo'], tree.get_parent_ids())
95
248
# the commit should have returned the same id we asked for.
96
249
self.assertEqual('foo', committed_id)
98
251
def test_commit_returns_revision_id(self):
99
252
tree = self.make_branch_and_tree('.')
100
committed_id = tree.commit('message', allow_pointless=True)
253
committed_id = tree.commit('message')
101
254
self.assertTrue(tree.branch.repository.has_revision(committed_id))
102
255
self.assertNotEqual(None, committed_id)
104
257
def test_commit_local_unbound(self):
105
# using the library api to do a local commit on unbound branches is
258
# using the library api to do a local commit on unbound branches is
107
260
tree = self.make_branch_and_tree('tree')
108
261
self.assertRaises(errors.LocalRequiresBoundBranch,
323
477
basis.get_reference_revision(basis.path2id('subtree')))
324
478
self.assertNotEqual(rev_id, rev_id2)
480
def test_nested_pointless_commits_are_pointless(self):
481
tree = self.make_branch_and_tree('.')
482
if not tree.supports_tree_reference():
485
subtree = self.make_branch_and_tree('subtree')
486
tree.add(['subtree'])
487
# record the reference.
488
rev_id = tree.commit('added reference')
489
child_revid = subtree.last_revision()
490
# now do a no-op commit with allow_pointless=False
491
self.assertRaises(errors.PointlessCommit, tree.commit, '',
492
allow_pointless=False)
493
self.assertEqual(child_revid, subtree.last_revision())
494
self.assertEqual(rev_id, tree.last_revision())
327
497
class TestCommitProgress(TestCaseWithWorkingTree):
329
499
def restoreDefaults(self):
330
500
ui.ui_factory = self.old_ui_factory
332
502
def test_commit_progress_steps(self):
333
# during commit we one progress update for every entry in the
503
# during commit we one progress update for every entry in the
334
504
# inventory, and then one for the inventory, and one for the
335
505
# inventory, and one for the revision insertions.
336
# first we need a test commit to do. Lets setup a branch with
506
# first we need a test commit to do. Lets setup a branch with
337
507
# 3 files, and alter one in a selected-file commit. This exercises
338
# a number of cases quickly. We should also test things like
508
# a number of cases quickly. We should also test things like
339
509
# selective commits which excludes newly added files.
340
510
tree = self.make_branch_and_tree('.')
341
511
self.build_tree(['a', 'b', 'c'])
354
524
# into the factory for this test - just make the test ui factory
355
525
# pun as a reporter. Then we can check the ordering is right.
356
526
tree.commit('second post', specific_files=['b'])
357
# 4 steps, the first of which is reported 2 times, once per dir
527
# 5 steps, the first of which is reported 2 times, once per dir
358
528
self.assertEqual(
359
[('update', 1, 4, 'Collecting changes [Directory 0] - Stage'),
360
('update', 1, 4, 'Collecting changes [Directory 1] - Stage'),
361
('update', 2, 4, 'Saving data locally - Stage'),
362
('update', 3, 4, 'Updating the working tree - Stage'),
363
('update', 4, 4, 'Running post commit hooks - Stage')],
529
[('update', 1, 5, 'Collecting changes [0] - Stage'),
530
('update', 1, 5, 'Collecting changes [1] - Stage'),
531
('update', 2, 5, 'Saving data locally - Stage'),
532
('update', 3, 5, 'Running pre_commit hooks - Stage'),
533
('update', 4, 5, 'Updating the working tree - Stage'),
534
('update', 5, 5, 'Running post_commit hooks - Stage')],
367
def test_commit_progress_shows_hook_names(self):
538
def test_commit_progress_shows_post_hook_names(self):
368
539
tree = self.make_branch_and_tree('.')
369
# set a progress bar that captures the calls so we can see what is
540
# set a progress bar that captures the calls so we can see what is
371
542
self.old_ui_factory = ui.ui_factory
372
543
self.addCleanup(self.restoreDefaults)
374
545
ui.ui_factory = factory
375
546
def a_hook(_, _2, _3, _4, _5, _6):
377
branch.Branch.hooks.install_hook('post_commit', a_hook)
378
branch.Branch.hooks.name_hook(a_hook, 'hook name')
379
tree.commit('first post')
381
[('update', 1, 4, 'Collecting changes [Directory 0] - Stage'),
382
('update', 1, 4, 'Collecting changes [Directory 1] - Stage'),
383
('update', 2, 4, 'Saving data locally - Stage'),
384
('update', 3, 4, 'Updating the working tree - Stage'),
385
('update', 4, 4, 'Running post commit hooks - Stage'),
386
('update', 4, 4, 'Running post commit hooks [hook name] - Stage'),
548
branch.Branch.hooks.install_named_hook('post_commit', a_hook,
550
tree.commit('first post')
552
[('update', 1, 5, 'Collecting changes [0] - Stage'),
553
('update', 1, 5, 'Collecting changes [1] - Stage'),
554
('update', 2, 5, 'Saving data locally - Stage'),
555
('update', 3, 5, 'Running pre_commit hooks - Stage'),
556
('update', 4, 5, 'Updating the working tree - Stage'),
557
('update', 5, 5, 'Running post_commit hooks - Stage'),
558
('update', 5, 5, 'Running post_commit hooks [hook name] - Stage'),
563
def test_commit_progress_shows_pre_hook_names(self):
564
tree = self.make_branch_and_tree('.')
565
# set a progress bar that captures the calls so we can see what is
567
self.old_ui_factory = ui.ui_factory
568
self.addCleanup(self.restoreDefaults)
569
factory = CapturingUIFactory()
570
ui.ui_factory = factory
571
def a_hook(_, _2, _3, _4, _5, _6, _7, _8):
573
branch.Branch.hooks.install_named_hook('pre_commit', a_hook,
575
tree.commit('first post')
577
[('update', 1, 5, 'Collecting changes [0] - Stage'),
578
('update', 1, 5, 'Collecting changes [1] - Stage'),
579
('update', 2, 5, 'Saving data locally - Stage'),
580
('update', 3, 5, 'Running pre_commit hooks - Stage'),
581
('update', 3, 5, 'Running pre_commit hooks [hook name] - Stage'),
582
('update', 4, 5, 'Updating the working tree - Stage'),
583
('update', 5, 5, 'Running post_commit hooks - Stage'),
588
def test_start_commit_hook(self):
589
"""Make sure a start commit hook can modify the tree that is
591
def start_commit_hook_adds_file(tree):
592
open(tree.abspath("newfile"), 'w').write("data")
593
tree.add(["newfile"])
594
def restoreDefaults():
595
mutabletree.MutableTree.hooks['start_commit'] = []
596
self.addCleanup(restoreDefaults)
597
tree = self.make_branch_and_tree('.')
598
mutabletree.MutableTree.hooks.install_named_hook(
600
start_commit_hook_adds_file,
602
revid = tree.commit('first post')
603
committed_tree = tree.basis_tree()
604
self.assertTrue(committed_tree.has_filename("newfile"))