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."""
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.script import (
31
class TestUncommit(TestCaseWithTransport):
33
def create_simple_tree(self):
34
wt = self.make_branch_and_tree('tree')
35
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
36
wt.add(['a', 'b', 'c'])
37
wt.commit('initial commit', rev_id=b'a1')
39
self.build_tree_contents([('tree/a', b'new contents of a\n')])
40
wt.commit('second commit', rev_id=b'a2')
44
def test_uncommit(self):
45
"""Test uncommit functionality."""
46
wt = self.create_simple_tree()
49
out, err = self.run_bzr('uncommit --dry-run --force')
50
self.assertContainsRe(out, 'Dry-run')
51
self.assertNotContainsRe(out, 'initial commit')
52
self.assertContainsRe(out, 'second commit')
55
self.assertEqual([b'a2'], wt.get_parent_ids())
57
# Uncommit, don't prompt
58
out, err = self.run_bzr('uncommit --force')
59
self.assertNotContainsRe(out, 'initial commit')
60
self.assertContainsRe(out, 'second commit')
62
# This should look like we are back in revno 1
63
self.assertEqual([b'a1'], wt.get_parent_ids())
64
out, err = self.run_bzr('status')
65
self.assertEqual(out, 'modified:\n a\n')
67
def test_uncommit_interactive(self):
68
"""Uncommit seeks confirmation, and doesn't proceed without it."""
69
wt = self.create_simple_tree()
74
The above revision(s) will be removed.
75
2>Uncommit these revisions? ([y]es, [n]o): no
79
self.assertEqual([b'a2'], wt.get_parent_ids())
81
def test_uncommit_no_history(self):
82
wt = self.make_branch_and_tree('tree')
83
out, err = self.run_bzr('uncommit --force', retcode=1)
84
self.assertEqual('', err)
85
self.assertEqual('No revisions to uncommit.\n', out)
87
def test_uncommit_checkout(self):
88
wt = self.create_simple_tree()
89
checkout_tree = wt.branch.create_checkout('checkout')
91
self.assertEqual([b'a2'], checkout_tree.get_parent_ids())
94
out, err = self.run_bzr('uncommit --dry-run --force')
95
self.assertContainsRe(out, 'Dry-run')
96
self.assertNotContainsRe(out, 'initial commit')
97
self.assertContainsRe(out, 'second commit')
99
self.assertEqual([b'a2'], checkout_tree.get_parent_ids())
101
out, err = self.run_bzr('uncommit --force')
102
self.assertNotContainsRe(out, 'initial commit')
103
self.assertContainsRe(out, 'second commit')
105
# uncommit in a checkout should uncommit the parent branch
106
# (but doesn't effect the other working tree)
107
self.assertEqual([b'a1'], checkout_tree.get_parent_ids())
108
self.assertEqual(b'a1', wt.branch.last_revision())
109
self.assertEqual([b'a2'], wt.get_parent_ids())
111
def test_uncommit_bound(self):
113
a = BzrDirMetaFormat1().initialize('a')
114
a.create_repository()
116
t_a = a.create_workingtree()
117
t_a.commit('commit 1')
118
t_a.commit('commit 2')
119
t_a.commit('commit 3')
120
b = t_a.branch.create_checkout('b').branch
122
self.assertEqual(b.last_revision_info()[0], 2)
123
self.assertEqual(t_a.branch.last_revision_info()[0], 2)
124
# update A's tree to not have the uncommitted revision referenced.
126
t_a.commit('commit 3b')
127
self.assertRaises(BoundBranchOutOfDate, uncommit.uncommit, b)
131
def test_uncommit_bound_local(self):
132
t_a = self.make_branch_and_tree('a')
133
rev_id1 = t_a.commit('commit 1')
134
rev_id2 = t_a.commit('commit 2')
135
rev_id3 = t_a.commit('commit 3')
136
b = t_a.branch.create_checkout('b').branch
138
out, err = self.run_bzr(['uncommit', '--local', 'b', '--force'])
139
self.assertEqual(rev_id3, t_a.last_revision())
140
self.assertEqual((3, rev_id3), t_a.branch.last_revision_info())
141
self.assertEqual((2, rev_id2), b.last_revision_info())
143
def test_uncommit_revision(self):
144
wt = self.create_simple_tree()
147
out, err = self.run_bzr('uncommit -r1 --force')
149
self.assertNotContainsRe(out, 'initial commit')
150
self.assertContainsRe(out, 'second commit')
151
self.assertEqual([b'a1'], wt.get_parent_ids())
152
self.assertEqual(b'a1', wt.branch.last_revision())
154
def test_uncommit_neg_1(self):
155
wt = self.create_simple_tree()
157
out, err = self.run_bzr('uncommit -r -1', retcode=1)
158
self.assertEqual('No revisions to uncommit.\n', out)
160
def test_uncommit_merges(self):
161
wt = self.create_simple_tree()
163
tree2 = wt.controldir.sprout('tree2').open_workingtree()
165
tree2.commit('unchanged', rev_id=b'b3')
166
tree2.commit('unchanged', rev_id=b'b4')
168
wt.merge_from_branch(tree2.branch)
169
wt.commit('merge b4', rev_id=b'a3')
171
self.assertEqual([b'a3'], wt.get_parent_ids())
174
out, err = self.run_bzr('uncommit --force')
176
self.assertEqual([b'a2', b'b4'], wt.get_parent_ids())
178
def test_uncommit_pending_merge(self):
179
wt = self.create_simple_tree()
180
tree2 = wt.controldir.sprout('tree2').open_workingtree()
181
tree2.commit('unchanged', rev_id=b'b3')
183
wt.branch.fetch(tree2.branch)
184
wt.set_pending_merges([b'b3'])
187
out, err = self.run_bzr('uncommit --force')
188
self.assertEqual([b'a1', b'b3'], wt.get_parent_ids())
190
def test_uncommit_multiple_merge(self):
191
wt = self.create_simple_tree()
193
tree2 = wt.controldir.sprout('tree2').open_workingtree()
194
tree2.commit('unchanged', rev_id=b'b3')
196
tree3 = wt.controldir.sprout('tree3').open_workingtree()
197
tree3.commit('unchanged', rev_id=b'c3')
199
wt.merge_from_branch(tree2.branch)
200
wt.commit('merge b3', rev_id=b'a3')
202
wt.merge_from_branch(tree3.branch)
203
wt.commit('merge c3', rev_id=b'a4')
205
self.assertEqual([b'a4'], wt.get_parent_ids())
208
out, err = self.run_bzr('uncommit --force -r 2')
210
self.assertEqual([b'a2', b'b3', b'c3'], wt.get_parent_ids())
212
def test_uncommit_merge_plus_pending(self):
213
wt = self.create_simple_tree()
215
tree2 = wt.controldir.sprout('tree2').open_workingtree()
216
tree2.commit('unchanged', rev_id=b'b3')
217
tree3 = wt.controldir.sprout('tree3').open_workingtree()
218
tree3.commit('unchanged', rev_id=b'c3')
220
wt.branch.fetch(tree2.branch)
221
wt.set_pending_merges([b'b3'])
222
wt.commit('merge b3', rev_id=b'a3')
224
wt.merge_from_branch(tree3.branch)
226
self.assertEqual([b'a3', b'c3'], wt.get_parent_ids())
229
out, err = self.run_bzr('uncommit --force -r 2')
231
self.assertEqual([b'a2', b'b3', b'c3'], wt.get_parent_ids())
233
def test_uncommit_shows_log_with_revision_id(self):
234
wt = self.create_simple_tree()
236
script = ScriptRunner()
237
script.run_script(self, """
239
$ brz uncommit --force
243
The above revision(s) will be removed.
244
You can restore the old tip by running:
245
brz pull . -r revid:a2
248
def test_uncommit_shows_pull_with_location(self):
249
wt = self.create_simple_tree()
251
script = ScriptRunner()
252
script.run_script(self, """
253
$ brz uncommit --force tree
257
The above revision(s) will be removed.
258
You can restore the old tip by running:
259
brz pull -d tree tree -r revid:a2
262
def test_uncommit_octopus_merge(self):
263
# Check that uncommit keeps the pending merges in the same order
264
# though it will also filter out ones in the ancestry
265
wt = self.create_simple_tree()
267
tree2 = wt.controldir.sprout('tree2').open_workingtree()
268
tree3 = wt.controldir.sprout('tree3').open_workingtree()
270
tree2.commit('unchanged', rev_id=b'b3')
271
tree3.commit('unchanged', rev_id=b'c3')
273
wt.merge_from_branch(tree2.branch)
274
wt.merge_from_branch(tree3.branch, force=True)
275
wt.commit('merge b3, c3', rev_id=b'a3')
277
tree2.commit('unchanged', rev_id=b'b4')
278
tree3.commit('unchanged', rev_id=b'c4')
280
wt.merge_from_branch(tree3.branch)
281
wt.merge_from_branch(tree2.branch, force=True)
282
wt.commit('merge b4, c4', rev_id=b'a4')
284
self.assertEqual([b'a4'], wt.get_parent_ids())
287
out, err = self.run_bzr('uncommit --force -r 2')
289
self.assertEqual([b'a2', b'c4', b'b4'], wt.get_parent_ids())
291
def test_uncommit_nonascii(self):
292
tree = self.make_branch_and_tree('tree')
293
tree.commit(u'\u1234 message')
294
out, err = self.run_bzr('uncommit --force tree', encoding='ascii')
295
self.assertContainsRe(out, r'\? message')
297
def test_uncommit_removes_tags(self):
298
tree = self.make_branch_and_tree('tree')
299
revid = tree.commit('message')
300
tree.branch.tags.set_tag("atag", revid)
301
out, err = self.run_bzr('uncommit --force tree')
302
self.assertEqual({}, tree.branch.tags.get_tag_dict())
304
def test_uncommit_keep_tags(self):
305
tree = self.make_branch_and_tree('tree')
306
revid = tree.commit('message')
307
tree.branch.tags.set_tag("atag", revid)
308
out, err = self.run_bzr('uncommit --keep-tags --force tree')
309
self.assertEqual({"atag": revid}, tree.branch.tags.get_tag_dict())
312
class TestInconsistentDelta(TestCaseWithTransport):
313
# See https://bugs.launchpad.net/bzr/+bug/855155
314
# See https://bugs.launchpad.net/bzr/+bug/1100385
315
# brz uncommit may result in error
316
# 'An inconsistent delta was supplied involving'
318
def test_inconsistent_delta(self):
319
# Script taken from https://bugs.launchpad.net/bzr/+bug/855155/comments/26
320
wt = self.make_branch_and_tree('test')
321
self.build_tree(['test/a/', 'test/a/b', 'test/a/c'])
322
wt.add(['a', 'a/b', 'a/c'])
323
wt.commit('initial commit', rev_id=b'a1')
324
wt.remove(['a/b', 'a/c'])
325
wt.commit('remove b and c', rev_id=b'a2')
326
self.run_bzr("uncommit --force test")