diff options
Diffstat (limited to 'bzrlib/tests/blackbox/test_uncommit.py')
-rw-r--r-- | bzrlib/tests/blackbox/test_uncommit.py | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/bzrlib/tests/blackbox/test_uncommit.py b/bzrlib/tests/blackbox/test_uncommit.py new file mode 100644 index 0000000..1f10579 --- /dev/null +++ b/bzrlib/tests/blackbox/test_uncommit.py @@ -0,0 +1,316 @@ +# Copyright (C) 2005-2010 Canonical Ltd +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +"""Test the uncommit command.""" + +import os + +from bzrlib import uncommit +from bzrlib.bzrdir import BzrDirMetaFormat1 +from bzrlib.errors import BoundBranchOutOfDate +from bzrlib.tests import TestCaseWithTransport +from bzrlib.tests.matchers import ContainsNoVfsCalls +from bzrlib.tests.script import ( + run_script, + ScriptRunner, + ) + + +class TestUncommit(TestCaseWithTransport): + + def create_simple_tree(self): + wt = self.make_branch_and_tree('tree') + self.build_tree(['tree/a', 'tree/b', 'tree/c']) + wt.add(['a', 'b', 'c']) + wt.commit('initial commit', rev_id='a1') + + self.build_tree_contents([('tree/a', 'new contents of a\n')]) + wt.commit('second commit', rev_id='a2') + + return wt + + def test_uncommit(self): + """Test uncommit functionality.""" + wt = self.create_simple_tree() + + os.chdir('tree') + out, err = self.run_bzr('uncommit --dry-run --force') + self.assertContainsRe(out, 'Dry-run') + self.assertNotContainsRe(out, 'initial commit') + self.assertContainsRe(out, 'second commit') + + # Nothing has changed + self.assertEqual(['a2'], wt.get_parent_ids()) + + # Uncommit, don't prompt + out, err = self.run_bzr('uncommit --force') + self.assertNotContainsRe(out, 'initial commit') + self.assertContainsRe(out, 'second commit') + + # This should look like we are back in revno 1 + self.assertEqual(['a1'], wt.get_parent_ids()) + out, err = self.run_bzr('status') + self.assertEquals(out, 'modified:\n a\n') + + def test_uncommit_interactive(self): + """Uncommit seeks confirmation, and doesn't proceed without it.""" + wt = self.create_simple_tree() + os.chdir('tree') + run_script(self, """ + $ bzr uncommit + ... + The above revision(s) will be removed. + 2>Uncommit these revisions? ([y]es, [n]o): no + <n + Canceled + """) + self.assertEqual(['a2'], wt.get_parent_ids()) + + def test_uncommit_no_history(self): + wt = self.make_branch_and_tree('tree') + out, err = self.run_bzr('uncommit --force', retcode=1) + self.assertEqual('', err) + self.assertEqual('No revisions to uncommit.\n', out) + + def test_uncommit_checkout(self): + wt = self.create_simple_tree() + checkout_tree = wt.branch.create_checkout('checkout') + + self.assertEqual(['a2'], checkout_tree.get_parent_ids()) + + os.chdir('checkout') + out, err = self.run_bzr('uncommit --dry-run --force') + self.assertContainsRe(out, 'Dry-run') + self.assertNotContainsRe(out, 'initial commit') + self.assertContainsRe(out, 'second commit') + + self.assertEqual(['a2'], checkout_tree.get_parent_ids()) + + out, err = self.run_bzr('uncommit --force') + self.assertNotContainsRe(out, 'initial commit') + self.assertContainsRe(out, 'second commit') + + # uncommit in a checkout should uncommit the parent branch + # (but doesn't effect the other working tree) + self.assertEquals(['a1'], checkout_tree.get_parent_ids()) + self.assertEquals('a1', wt.branch.last_revision()) + self.assertEquals(['a2'], wt.get_parent_ids()) + + def test_uncommit_bound(self): + os.mkdir('a') + a = BzrDirMetaFormat1().initialize('a') + a.create_repository() + a.create_branch() + t_a = a.create_workingtree() + t_a.commit('commit 1') + t_a.commit('commit 2') + t_a.commit('commit 3') + b = t_a.branch.create_checkout('b').branch + uncommit.uncommit(b) + self.assertEqual(b.last_revision_info()[0], 2) + self.assertEqual(t_a.branch.last_revision_info()[0], 2) + # update A's tree to not have the uncommitted revision referenced. + t_a.update() + t_a.commit('commit 3b') + self.assertRaises(BoundBranchOutOfDate, uncommit.uncommit, b) + b.pull(t_a.branch) + uncommit.uncommit(b) + + def test_uncommit_bound_local(self): + t_a = self.make_branch_and_tree('a') + rev_id1 = t_a.commit('commit 1') + rev_id2 = t_a.commit('commit 2') + rev_id3 = t_a.commit('commit 3') + b = t_a.branch.create_checkout('b').branch + + out, err = self.run_bzr(['uncommit', '--local', 'b', '--force']) + self.assertEqual(rev_id3, t_a.last_revision()) + self.assertEqual((3, rev_id3), t_a.branch.last_revision_info()) + self.assertEqual((2, rev_id2), b.last_revision_info()) + + def test_uncommit_revision(self): + wt = self.create_simple_tree() + + os.chdir('tree') + out, err = self.run_bzr('uncommit -r1 --force') + + self.assertNotContainsRe(out, 'initial commit') + self.assertContainsRe(out, 'second commit') + self.assertEqual(['a1'], wt.get_parent_ids()) + self.assertEqual('a1', wt.branch.last_revision()) + + def test_uncommit_neg_1(self): + wt = self.create_simple_tree() + os.chdir('tree') + out, err = self.run_bzr('uncommit -r -1', retcode=1) + self.assertEqual('No revisions to uncommit.\n', out) + + def test_uncommit_merges(self): + wt = self.create_simple_tree() + + tree2 = wt.bzrdir.sprout('tree2').open_workingtree() + + tree2.commit('unchanged', rev_id='b3') + tree2.commit('unchanged', rev_id='b4') + + wt.merge_from_branch(tree2.branch) + wt.commit('merge b4', rev_id='a3') + + self.assertEqual(['a3'], wt.get_parent_ids()) + + os.chdir('tree') + out, err = self.run_bzr('uncommit --force') + + self.assertEqual(['a2', 'b4'], wt.get_parent_ids()) + + def test_uncommit_pending_merge(self): + wt = self.create_simple_tree() + tree2 = wt.bzrdir.sprout('tree2').open_workingtree() + tree2.commit('unchanged', rev_id='b3') + + wt.branch.fetch(tree2.branch) + wt.set_pending_merges(['b3']) + + os.chdir('tree') + out, err = self.run_bzr('uncommit --force') + self.assertEqual(['a1', 'b3'], wt.get_parent_ids()) + + def test_uncommit_multiple_merge(self): + wt = self.create_simple_tree() + + tree2 = wt.bzrdir.sprout('tree2').open_workingtree() + tree2.commit('unchanged', rev_id='b3') + + tree3 = wt.bzrdir.sprout('tree3').open_workingtree() + tree3.commit('unchanged', rev_id='c3') + + wt.merge_from_branch(tree2.branch) + wt.commit('merge b3', rev_id='a3') + + wt.merge_from_branch(tree3.branch) + wt.commit('merge c3', rev_id='a4') + + self.assertEqual(['a4'], wt.get_parent_ids()) + + os.chdir('tree') + out, err = self.run_bzr('uncommit --force -r 2') + + self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids()) + + def test_uncommit_merge_plus_pending(self): + wt = self.create_simple_tree() + + tree2 = wt.bzrdir.sprout('tree2').open_workingtree() + tree2.commit('unchanged', rev_id='b3') + tree3 = wt.bzrdir.sprout('tree3').open_workingtree() + tree3.commit('unchanged', rev_id='c3') + + wt.branch.fetch(tree2.branch) + wt.set_pending_merges(['b3']) + wt.commit('merge b3', rev_id='a3') + + + wt.merge_from_branch(tree3.branch) + + self.assertEqual(['a3', 'c3'], wt.get_parent_ids()) + + os.chdir('tree') + out, err = self.run_bzr('uncommit --force -r 2') + + self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids()) + + def test_uncommit_shows_log_with_revision_id(self): + wt = self.create_simple_tree() + + script = ScriptRunner() + script.run_script(self, """ +$ cd tree +$ bzr uncommit --force + 2 ... + second commit +... +The above revision(s) will be removed. +You can restore the old tip by running: + bzr pull . -r revid:a2 +""") + + def test_uncommit_octopus_merge(self): + # Check that uncommit keeps the pending merges in the same order + # though it will also filter out ones in the ancestry + wt = self.create_simple_tree() + + tree2 = wt.bzrdir.sprout('tree2').open_workingtree() + tree3 = wt.bzrdir.sprout('tree3').open_workingtree() + + tree2.commit('unchanged', rev_id='b3') + tree3.commit('unchanged', rev_id='c3') + + wt.merge_from_branch(tree2.branch) + wt.merge_from_branch(tree3.branch, force=True) + wt.commit('merge b3, c3', rev_id='a3') + + tree2.commit('unchanged', rev_id='b4') + tree3.commit('unchanged', rev_id='c4') + + wt.merge_from_branch(tree3.branch) + wt.merge_from_branch(tree2.branch, force=True) + wt.commit('merge b4, c4', rev_id='a4') + + self.assertEqual(['a4'], wt.get_parent_ids()) + + os.chdir('tree') + out, err = self.run_bzr('uncommit --force -r 2') + + self.assertEqual(['a2', 'c4', 'b4'], wt.get_parent_ids()) + + def test_uncommit_nonascii(self): + tree = self.make_branch_and_tree('tree') + tree.commit(u'\u1234 message') + out, err = self.run_bzr('uncommit --force tree', encoding='ascii') + self.assertContainsRe(out, r'\? message') + + def test_uncommit_removes_tags(self): + tree = self.make_branch_and_tree('tree') + revid = tree.commit('message') + tree.branch.tags.set_tag("atag", revid) + out, err = self.run_bzr('uncommit --force tree') + self.assertEquals({}, tree.branch.tags.get_tag_dict()) + + def test_uncommit_keep_tags(self): + tree = self.make_branch_and_tree('tree') + revid = tree.commit('message') + tree.branch.tags.set_tag("atag", revid) + out, err = self.run_bzr('uncommit --keep-tags --force tree') + self.assertEquals({"atag": revid}, tree.branch.tags.get_tag_dict()) + + +class TestSmartServerUncommit(TestCaseWithTransport): + + def test_uncommit(self): + self.setup_smart_server_with_call_log() + t = self.make_branch_and_tree('from') + for count in range(2): + t.commit(message='commit %d' % count) + self.reset_smart_call_log() + out, err = self.run_bzr(['uncommit', '--force', self.get_url('from')]) + # This figure represent the amount of work to perform this use case. It + # is entirely ok to reduce this number if a test fails due to rpc_count + # being too low. If rpc_count increases, more network roundtrips have + # become necessary for this use case. Please do not adjust this number + # upwards without agreement from bzr's network support maintainers. + self.assertLength(14, self.hpss_calls) + self.assertLength(1, self.hpss_connections) + self.assertThat(self.hpss_calls, ContainsNoVfsCalls) |