1
# Copyright (C) 2005-2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Test the uncommit command."""
21
from bzrlib import uncommit, workingtree
22
from bzrlib.bzrdir import BzrDirMetaFormat1
23
from bzrlib.errors import BzrError, BoundBranchOutOfDate
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.script import ScriptRunner
28
class TestUncommit(TestCaseWithTransport):
30
def create_simple_tree(self):
31
wt = self.make_branch_and_tree('tree')
32
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
33
wt.add(['a', 'b', 'c'])
34
wt.commit('initial commit', rev_id='a1')
36
self.build_tree_contents([('tree/a', 'new contents of a\n')])
37
wt.commit('second commit', rev_id='a2')
41
def test_uncommit(self):
42
"""Test uncommit functionality."""
43
wt = self.create_simple_tree()
46
out, err = self.run_bzr('uncommit --dry-run --force')
47
self.assertContainsRe(out, 'Dry-run')
48
self.assertNotContainsRe(out, 'initial commit')
49
self.assertContainsRe(out, 'second commit')
52
self.assertEqual(['a2'], wt.get_parent_ids())
54
# Uncommit, don't prompt
55
out, err = self.run_bzr('uncommit --force')
56
self.assertNotContainsRe(out, 'initial commit')
57
self.assertContainsRe(out, 'second commit')
59
# This should look like we are back in revno 1
60
self.assertEqual(['a1'], wt.get_parent_ids())
61
out, err = self.run_bzr('status')
62
self.assertEquals(out, 'modified:\n a\n')
64
def test_uncommit_no_history(self):
65
wt = self.make_branch_and_tree('tree')
66
out, err = self.run_bzr('uncommit --force', retcode=1)
67
self.assertEqual('', err)
68
self.assertEqual('No revisions to uncommit.\n', out)
70
def test_uncommit_checkout(self):
71
wt = self.create_simple_tree()
72
checkout_tree = wt.branch.create_checkout('checkout')
74
self.assertEqual(['a2'], checkout_tree.get_parent_ids())
77
out, err = self.run_bzr('uncommit --dry-run --force')
78
self.assertContainsRe(out, 'Dry-run')
79
self.assertNotContainsRe(out, 'initial commit')
80
self.assertContainsRe(out, 'second commit')
82
self.assertEqual(['a2'], checkout_tree.get_parent_ids())
84
out, err = self.run_bzr('uncommit --force')
85
self.assertNotContainsRe(out, 'initial commit')
86
self.assertContainsRe(out, 'second commit')
88
# uncommit in a checkout should uncommit the parent branch
89
# (but doesn't effect the other working tree)
90
self.assertEquals(['a1'], checkout_tree.get_parent_ids())
91
self.assertEquals('a1', wt.branch.last_revision())
92
self.assertEquals(['a2'], wt.get_parent_ids())
94
def test_uncommit_bound(self):
96
a = BzrDirMetaFormat1().initialize('a')
99
t_a = a.create_workingtree()
100
t_a.commit('commit 1')
101
t_a.commit('commit 2')
102
t_a.commit('commit 3')
103
b = t_a.branch.create_checkout('b').branch
105
self.assertEqual(len(b.revision_history()), 2)
106
self.assertEqual(len(t_a.branch.revision_history()), 2)
107
# update A's tree to not have the uncommitted revision referenced.
109
t_a.commit('commit 3b')
110
self.assertRaises(BoundBranchOutOfDate, uncommit.uncommit, b)
114
def test_uncommit_bound_local(self):
115
t_a = self.make_branch_and_tree('a')
116
rev_id1 = t_a.commit('commit 1')
117
rev_id2 = t_a.commit('commit 2')
118
rev_id3 = t_a.commit('commit 3')
119
b = t_a.branch.create_checkout('b').branch
121
out, err = self.run_bzr(['uncommit', '--local', 'b', '--force'])
122
self.assertEqual(rev_id3, t_a.last_revision())
123
self.assertEqual((3, rev_id3), t_a.branch.last_revision_info())
124
self.assertEqual((2, rev_id2), b.last_revision_info())
126
def test_uncommit_revision(self):
127
wt = self.create_simple_tree()
130
out, err = self.run_bzr('uncommit -r1 --force')
132
self.assertNotContainsRe(out, 'initial commit')
133
self.assertContainsRe(out, 'second commit')
134
self.assertEqual(['a1'], wt.get_parent_ids())
135
self.assertEqual('a1', wt.branch.last_revision())
137
def test_uncommit_neg_1(self):
138
wt = self.create_simple_tree()
140
out, err = self.run_bzr('uncommit -r -1', retcode=1)
141
self.assertEqual('No revisions to uncommit.\n', out)
143
def test_uncommit_merges(self):
144
wt = self.create_simple_tree()
146
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
148
tree2.commit('unchanged', rev_id='b3')
149
tree2.commit('unchanged', rev_id='b4')
151
wt.merge_from_branch(tree2.branch)
152
wt.commit('merge b4', rev_id='a3')
154
self.assertEqual(['a3'], wt.get_parent_ids())
157
out, err = self.run_bzr('uncommit --force')
159
self.assertEqual(['a2', 'b4'], wt.get_parent_ids())
161
def test_uncommit_pending_merge(self):
162
wt = self.create_simple_tree()
163
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
164
tree2.commit('unchanged', rev_id='b3')
166
wt.branch.fetch(tree2.branch)
167
wt.set_pending_merges(['b3'])
170
out, err = self.run_bzr('uncommit --force')
171
self.assertEqual(['a1', 'b3'], wt.get_parent_ids())
173
def test_uncommit_multiple_merge(self):
174
wt = self.create_simple_tree()
176
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
177
tree2.commit('unchanged', rev_id='b3')
179
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
180
tree3.commit('unchanged', rev_id='c3')
182
wt.merge_from_branch(tree2.branch)
183
wt.commit('merge b3', rev_id='a3')
185
wt.merge_from_branch(tree3.branch)
186
wt.commit('merge c3', rev_id='a4')
188
self.assertEqual(['a4'], wt.get_parent_ids())
191
out, err = self.run_bzr('uncommit --force -r 2')
193
self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
195
def test_uncommit_merge_plus_pending(self):
196
wt = self.create_simple_tree()
198
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
199
tree2.commit('unchanged', rev_id='b3')
200
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
201
tree3.commit('unchanged', rev_id='c3')
203
wt.branch.fetch(tree2.branch)
204
wt.set_pending_merges(['b3'])
205
wt.commit('merge b3', rev_id='a3')
208
wt.merge_from_branch(tree3.branch)
210
self.assertEqual(['a3', 'c3'], wt.get_parent_ids())
213
out, err = self.run_bzr('uncommit --force -r 2')
215
self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
217
def test_uncommit_shows_log_with_revision_id(self):
218
wt = self.create_simple_tree()
220
script = ScriptRunner()
221
script.run_script(self, """
223
$ bzr uncommit --force
227
The above revision(s) will be removed.
228
You can restore the old tip by running:
229
bzr pull . -r revid:a2
232
def test_uncommit_octopus_merge(self):
233
# Check that uncommit keeps the pending merges in the same order
234
# though it will also filter out ones in the ancestry
235
wt = self.create_simple_tree()
237
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
238
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
240
tree2.commit('unchanged', rev_id='b3')
241
tree3.commit('unchanged', rev_id='c3')
243
wt.merge_from_branch(tree2.branch)
244
wt.merge_from_branch(tree3.branch, force=True)
245
wt.commit('merge b3, c3', rev_id='a3')
247
tree2.commit('unchanged', rev_id='b4')
248
tree3.commit('unchanged', rev_id='c4')
250
wt.merge_from_branch(tree3.branch)
251
wt.merge_from_branch(tree2.branch, force=True)
252
wt.commit('merge b4, c4', rev_id='a4')
254
self.assertEqual(['a4'], wt.get_parent_ids())
257
out, err = self.run_bzr('uncommit --force -r 2')
259
self.assertEqual(['a2', 'c4', 'b4'], wt.get_parent_ids())
261
def test_uncommit_nonascii(self):
262
tree = self.make_branch_and_tree('tree')
263
tree.commit(u'\u1234 message')
264
out, err = self.run_bzr('uncommit --force tree', encoding='ascii')
265
self.assertContainsRe(out, r'\? message')