1
# Copyright (C) 2005-2011, 2013, 2016 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."""
2
Test the uncommit command.
21
from breezy import uncommit
22
from breezy.bzr.bzrdir import BzrDirMetaFormat1
23
from breezy.errors import BoundBranchOutOfDate
24
from breezy.tests import TestCaseWithTransport
25
from breezy.tests.matchers import ContainsNoVfsCalls
26
from breezy.tests.script import (
32
class TestUncommit(TestCaseWithTransport):
34
def create_simple_tree(self):
35
wt = self.make_branch_and_tree('tree')
36
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
37
wt.add(['a', 'b', 'c'])
38
wt.commit('initial commit', rev_id=b'a1')
40
self.build_tree_contents([('tree/a', b'new contents of a\n')])
41
wt.commit('second commit', rev_id=b'a2')
7
from bzrlib.bzrdir import BzrDirMetaFormat1
8
from bzrlib.errors import BzrError, BoundBranchOutOfDate
9
from bzrlib.uncommit import uncommit
10
from bzrlib.tests import TestCaseInTempDir
12
class TestUncommit(TestCaseInTempDir):
45
13
def test_uncommit(self):
46
14
"""Test uncommit functionality."""
47
wt = self.create_simple_tree()
50
out, err = self.run_bzr('uncommit --dry-run --force')
51
self.assertContainsRe(out, 'Dry-run')
52
self.assertNotContainsRe(out, 'initial commit')
53
self.assertContainsRe(out, 'second commit')
56
self.assertEqual([b'a2'], wt.get_parent_ids())
58
# Uncommit, don't prompt
59
out, err = self.run_bzr('uncommit --force')
60
self.assertNotContainsRe(out, 'initial commit')
61
self.assertContainsRe(out, 'second commit')
63
# This should look like we are back in revno 1
64
self.assertEqual([b'a1'], wt.get_parent_ids())
65
out, err = self.run_bzr('status')
66
self.assertEqual(out, 'modified:\n a\n')
68
def test_uncommit_interactive(self):
69
"""Uncommit seeks confirmation, and doesn't proceed without it."""
70
wt = self.create_simple_tree()
75
The above revision(s) will be removed.
76
2>Uncommit these revisions? ([y]es, [n]o): no
80
self.assertEqual([b'a2'], wt.get_parent_ids())
82
def test_uncommit_no_history(self):
83
wt = self.make_branch_and_tree('tree')
84
out, err = self.run_bzr('uncommit --force', retcode=1)
85
self.assertEqual('', err)
86
self.assertEqual('No revisions to uncommit.\n', out)
88
def test_uncommit_checkout(self):
89
wt = self.create_simple_tree()
90
checkout_tree = wt.branch.create_checkout('checkout')
92
self.assertEqual([b'a2'], checkout_tree.get_parent_ids())
95
out, err = self.run_bzr('uncommit --dry-run --force')
96
self.assertContainsRe(out, 'Dry-run')
97
self.assertNotContainsRe(out, 'initial commit')
98
self.assertContainsRe(out, 'second commit')
100
self.assertEqual([b'a2'], checkout_tree.get_parent_ids())
102
out, err = self.run_bzr('uncommit --force')
103
self.assertNotContainsRe(out, 'initial commit')
104
self.assertContainsRe(out, 'second commit')
106
# uncommit in a checkout should uncommit the parent branch
107
# (but doesn't effect the other working tree)
108
self.assertEqual([b'a1'], checkout_tree.get_parent_ids())
109
self.assertEqual(b'a1', wt.branch.last_revision())
110
self.assertEqual([b'a2'], wt.get_parent_ids())
19
self.build_tree(['a', 'b', 'c'])
22
bzr('commit -m initial')
24
self.assertEquals(bzr('revno'), '1\n')
26
open('a', 'wb').write('new contents of a\n')
27
self.assertEquals(bzr('status'), 'modified:\n a\n')
28
bzr('commit -m second')
30
self.assertEquals(bzr('status'), '')
31
self.assertEquals(bzr('revno'), '2\n')
33
txt = bzr('uncommit --dry-run --force')
34
self.failIfEqual(txt.find('Dry-run'), -1)
36
self.assertEquals(bzr('status'), '')
37
self.assertEquals(bzr('revno'), '2\n')
39
txt = bzr('uncommit --force')
41
self.assertEquals(bzr('revno'), '1\n')
42
self.assertEquals(bzr('status'), 'modified:\n a\n')
44
bzr('checkout . ../checkout')
45
os.chdir('../checkout')
46
self.assertEquals("", bzr('status'))
47
self.assertEquals(bzr('revno'), '1\n')
49
open('a', 'wb').write('new contents of a\n')
50
self.assertEquals(bzr('status'), 'modified:\n a\n')
51
bzr('commit -m second')
53
self.assertEquals(bzr('status'), '')
54
self.assertEquals(bzr('revno'), '2\n')
56
txt = bzr('uncommit --dry-run --force')
57
self.failIfEqual(txt.find('Dry-run'), -1)
59
self.assertEquals(bzr('status'), '')
60
self.assertEquals(bzr('revno'), '2\n')
62
txt = bzr('uncommit --force')
64
self.assertEquals(bzr('revno'), '1\n')
65
self.assertEquals(bzr('status'), 'modified:\n a\n')
112
67
def test_uncommit_bound(self):
114
69
a = BzrDirMetaFormat1().initialize('a')
115
70
a.create_repository()
117
t_a = a.create_workingtree()
118
t_a.commit('commit 1')
119
t_a.commit('commit 2')
120
t_a.commit('commit 3')
121
b = t_a.branch.create_checkout('b').branch
123
self.assertEqual(b.last_revision_info()[0], 2)
124
self.assertEqual(t_a.branch.last_revision_info()[0], 2)
125
# update A's tree to not have the uncommitted revision referenced.
127
t_a.commit('commit 3b')
128
self.assertRaises(BoundBranchOutOfDate, uncommit.uncommit, b)
132
def test_uncommit_bound_local(self):
133
t_a = self.make_branch_and_tree('a')
134
rev_id1 = t_a.commit('commit 1')
135
rev_id2 = t_a.commit('commit 2')
136
rev_id3 = t_a.commit('commit 3')
137
b = t_a.branch.create_checkout('b').branch
139
out, err = self.run_bzr(['uncommit', '--local', 'b', '--force'])
140
self.assertEqual(rev_id3, t_a.last_revision())
141
self.assertEqual((3, rev_id3), t_a.branch.last_revision_info())
142
self.assertEqual((2, rev_id2), b.last_revision_info())
144
def test_uncommit_revision(self):
145
wt = self.create_simple_tree()
148
out, err = self.run_bzr('uncommit -r1 --force')
150
self.assertNotContainsRe(out, 'initial commit')
151
self.assertContainsRe(out, 'second commit')
152
self.assertEqual([b'a1'], wt.get_parent_ids())
153
self.assertEqual(b'a1', wt.branch.last_revision())
155
def test_uncommit_neg_1(self):
156
wt = self.create_simple_tree()
158
out, err = self.run_bzr('uncommit -r -1', retcode=1)
159
self.assertEqual('No revisions to uncommit.\n', out)
161
def test_uncommit_merges(self):
162
wt = self.create_simple_tree()
164
tree2 = wt.controldir.sprout('tree2').open_workingtree()
166
tree2.commit('unchanged', rev_id=b'b3')
167
tree2.commit('unchanged', rev_id=b'b4')
169
wt.merge_from_branch(tree2.branch)
170
wt.commit('merge b4', rev_id=b'a3')
172
self.assertEqual([b'a3'], wt.get_parent_ids())
175
out, err = self.run_bzr('uncommit --force')
177
self.assertEqual([b'a2', b'b4'], wt.get_parent_ids())
179
def test_uncommit_pending_merge(self):
180
wt = self.create_simple_tree()
181
tree2 = wt.controldir.sprout('tree2').open_workingtree()
182
tree2.commit('unchanged', rev_id=b'b3')
184
wt.branch.fetch(tree2.branch)
185
wt.set_pending_merges([b'b3'])
188
out, err = self.run_bzr('uncommit --force')
189
self.assertEqual([b'a1', b'b3'], wt.get_parent_ids())
191
def test_uncommit_multiple_merge(self):
192
wt = self.create_simple_tree()
194
tree2 = wt.controldir.sprout('tree2').open_workingtree()
195
tree2.commit('unchanged', rev_id=b'b3')
197
tree3 = wt.controldir.sprout('tree3').open_workingtree()
198
tree3.commit('unchanged', rev_id=b'c3')
200
wt.merge_from_branch(tree2.branch)
201
wt.commit('merge b3', rev_id=b'a3')
203
wt.merge_from_branch(tree3.branch)
204
wt.commit('merge c3', rev_id=b'a4')
206
self.assertEqual([b'a4'], wt.get_parent_ids())
209
out, err = self.run_bzr('uncommit --force -r 2')
211
self.assertEqual([b'a2', b'b3', b'c3'], wt.get_parent_ids())
213
def test_uncommit_merge_plus_pending(self):
214
wt = self.create_simple_tree()
216
tree2 = wt.controldir.sprout('tree2').open_workingtree()
217
tree2.commit('unchanged', rev_id=b'b3')
218
tree3 = wt.controldir.sprout('tree3').open_workingtree()
219
tree3.commit('unchanged', rev_id=b'c3')
221
wt.branch.fetch(tree2.branch)
222
wt.set_pending_merges([b'b3'])
223
wt.commit('merge b3', rev_id=b'a3')
225
wt.merge_from_branch(tree3.branch)
227
self.assertEqual([b'a3', b'c3'], wt.get_parent_ids())
230
out, err = self.run_bzr('uncommit --force -r 2')
232
self.assertEqual([b'a2', b'b3', b'c3'], wt.get_parent_ids())
234
def test_uncommit_shows_log_with_revision_id(self):
235
wt = self.create_simple_tree()
237
script = ScriptRunner()
238
script.run_script(self, """
240
$ brz uncommit --force
244
The above revision(s) will be removed.
245
You can restore the old tip by running:
246
brz pull . -r revid:a2
249
def test_uncommit_shows_pull_with_location(self):
250
wt = self.create_simple_tree()
252
script = ScriptRunner()
253
script.run_script(self, """
254
$ brz uncommit --force tree
258
The above revision(s) will be removed.
259
You can restore the old tip by running:
260
brz pull -d tree tree -r revid:a2
263
def test_uncommit_octopus_merge(self):
264
# Check that uncommit keeps the pending merges in the same order
265
# though it will also filter out ones in the ancestry
266
wt = self.create_simple_tree()
268
tree2 = wt.controldir.sprout('tree2').open_workingtree()
269
tree3 = wt.controldir.sprout('tree3').open_workingtree()
271
tree2.commit('unchanged', rev_id=b'b3')
272
tree3.commit('unchanged', rev_id=b'c3')
274
wt.merge_from_branch(tree2.branch)
275
wt.merge_from_branch(tree3.branch, force=True)
276
wt.commit('merge b3, c3', rev_id=b'a3')
278
tree2.commit('unchanged', rev_id=b'b4')
279
tree3.commit('unchanged', rev_id=b'c4')
281
wt.merge_from_branch(tree3.branch)
282
wt.merge_from_branch(tree2.branch, force=True)
283
wt.commit('merge b4, c4', rev_id=b'a4')
285
self.assertEqual([b'a4'], wt.get_parent_ids())
288
out, err = self.run_bzr('uncommit --force -r 2')
290
self.assertEqual([b'a2', b'c4', b'b4'], wt.get_parent_ids())
292
def test_uncommit_nonascii(self):
293
tree = self.make_branch_and_tree('tree')
294
tree.commit(u'\u1234 message')
295
out, err = self.run_bzr('uncommit --force tree', encoding='ascii')
296
self.assertContainsRe(out, r'\? message')
298
def test_uncommit_removes_tags(self):
299
tree = self.make_branch_and_tree('tree')
300
revid = tree.commit('message')
301
tree.branch.tags.set_tag("atag", revid)
302
out, err = self.run_bzr('uncommit --force tree')
303
self.assertEqual({}, tree.branch.tags.get_tag_dict())
305
def test_uncommit_keep_tags(self):
306
tree = self.make_branch_and_tree('tree')
307
revid = tree.commit('message')
308
tree.branch.tags.set_tag("atag", revid)
309
out, err = self.run_bzr('uncommit --keep-tags --force tree')
310
self.assertEqual({"atag": revid}, tree.branch.tags.get_tag_dict())
313
class TestSmartServerUncommit(TestCaseWithTransport):
315
def test_uncommit(self):
316
self.setup_smart_server_with_call_log()
317
t = self.make_branch_and_tree('from')
318
for count in range(2):
319
t.commit(message='commit %d' % count)
320
self.reset_smart_call_log()
321
out, err = self.run_bzr(['uncommit', '--force', self.get_url('from')])
322
# This figure represent the amount of work to perform this use case. It
323
# is entirely ok to reduce this number if a test fails due to rpc_count
324
# being too low. If rpc_count increases, more network roundtrips have
325
# become necessary for this use case. Please do not adjust this number
326
# upwards without agreement from bzr's network support maintainers.
327
self.assertLength(14, self.hpss_calls)
328
self.assertLength(1, self.hpss_connections)
329
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
332
class TestInconsistentDelta(TestCaseWithTransport):
333
# See https://bugs.launchpad.net/bzr/+bug/855155
334
# See https://bugs.launchpad.net/bzr/+bug/1100385
335
# brz uncommit may result in error
336
# 'An inconsistent delta was supplied involving'
338
def test_inconsistent_delta(self):
339
# Script taken from https://bugs.launchpad.net/bzr/+bug/855155/comments/26
340
wt = self.make_branch_and_tree('test')
341
self.build_tree(['test/a/', 'test/a/b', 'test/a/c'])
342
wt.add(['a', 'a/b', 'a/c'])
343
wt.commit('initial commit', rev_id=b'a1')
344
wt.remove(['a/b', 'a/c'])
345
wt.commit('remove b and c', rev_id=b'a2')
346
self.run_bzr("uncommit --force test")
72
t = a.create_workingtree()
76
b = t.bzrdir.sprout('b').open_branch()
79
t.set_last_revision(t.branch.last_revision())
80
self.assertEqual(len(b.revision_history()), 2)
81
self.assertEqual(len(t.branch.revision_history()), 2)
83
self.assertRaises(BoundBranchOutOfDate, uncommit, b)