summaryrefslogtreecommitdiff
path: root/bzrlib/tests/blackbox/test_uncommit.py
diff options
context:
space:
mode:
Diffstat (limited to 'bzrlib/tests/blackbox/test_uncommit.py')
-rw-r--r--bzrlib/tests/blackbox/test_uncommit.py316
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)