13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Test the uncommit command."""
21
from bzrlib import uncommit, workingtree
21
from bzrlib import uncommit
22
22
from bzrlib.bzrdir import BzrDirMetaFormat1
23
from bzrlib.errors import BzrError, BoundBranchOutOfDate
23
from bzrlib.errors import BoundBranchOutOfDate
24
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.matchers import ContainsNoVfsCalls
26
from bzrlib.tests.script import (
27
32
class TestUncommit(TestCaseWithTransport):
28
34
def create_simple_tree(self):
29
35
wt = self.make_branch_and_tree('tree')
30
36
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
31
37
wt.add(['a', 'b', 'c'])
32
38
wt.commit('initial commit', rev_id='a1')
34
open('tree/a', 'wb').write('new contents of a\n')
40
self.build_tree_contents([('tree/a', 'new contents of a\n')])
35
41
wt.commit('second commit', rev_id='a2')
41
47
wt = self.create_simple_tree()
44
out, err = self.run_bzr('uncommit', '--dry-run', '--force')
50
out, err = self.run_bzr('uncommit --dry-run --force')
45
51
self.assertContainsRe(out, 'Dry-run')
46
52
self.assertNotContainsRe(out, 'initial commit')
47
53
self.assertContainsRe(out, 'second commit')
49
55
# Nothing has changed
50
self.assertEqual('a2', wt.last_revision())
56
self.assertEqual(['a2'], wt.get_parent_ids())
52
58
# Uncommit, don't prompt
53
out, err = self.run_bzr('uncommit', '--force')
59
out, err = self.run_bzr('uncommit --force')
54
60
self.assertNotContainsRe(out, 'initial commit')
55
61
self.assertContainsRe(out, 'second commit')
57
63
# This should look like we are back in revno 1
58
self.assertEqual('a1', wt.last_revision())
64
self.assertEqual(['a1'], wt.get_parent_ids())
59
65
out, err = self.run_bzr('status')
60
66
self.assertEquals(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(['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)
62
88
def test_uncommit_checkout(self):
63
89
wt = self.create_simple_tree()
65
checkout_tree = wt.bzrdir.sprout('checkout').open_workingtree()
66
checkout_tree.branch.bind(wt.branch)
68
self.assertEqual('a2', checkout_tree.last_revision())
90
checkout_tree = wt.branch.create_checkout('checkout')
92
self.assertEqual(['a2'], checkout_tree.get_parent_ids())
70
94
os.chdir('checkout')
71
out, err = self.run_bzr('uncommit', '--dry-run', '--force')
95
out, err = self.run_bzr('uncommit --dry-run --force')
72
96
self.assertContainsRe(out, 'Dry-run')
73
97
self.assertNotContainsRe(out, 'initial commit')
74
98
self.assertContainsRe(out, 'second commit')
76
self.assertEqual('a2', checkout_tree.last_revision())
100
self.assertEqual(['a2'], checkout_tree.get_parent_ids())
78
out, err = self.run_bzr('uncommit', '--force')
102
out, err = self.run_bzr('uncommit --force')
79
103
self.assertNotContainsRe(out, 'initial commit')
80
104
self.assertContainsRe(out, 'second commit')
82
106
# uncommit in a checkout should uncommit the parent branch
83
107
# (but doesn't effect the other working tree)
84
self.assertEquals('a1', checkout_tree.last_revision())
108
self.assertEquals(['a1'], checkout_tree.get_parent_ids())
85
109
self.assertEquals('a1', wt.branch.last_revision())
86
self.assertEquals('a2', wt.last_revision())
110
self.assertEquals(['a2'], wt.get_parent_ids())
88
112
def test_uncommit_bound(self):
90
114
a = BzrDirMetaFormat1().initialize('a')
91
115
a.create_repository()
93
t = a.create_workingtree()
97
b = t.bzrdir.sprout('b').open_branch()
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
99
122
uncommit.uncommit(b)
100
t.set_last_revision(t.branch.last_revision())
101
self.assertEqual(len(b.revision_history()), 2)
102
self.assertEqual(len(t.branch.revision_history()), 2)
103
t.commit('commit 3b')
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')
104
128
self.assertRaises(BoundBranchOutOfDate, uncommit.uncommit, b)
106
130
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())
108
144
def test_uncommit_revision(self):
109
145
wt = self.create_simple_tree()
112
out, err = self.run_bzr('uncommit', '-r1', '--force')
148
out, err = self.run_bzr('uncommit -r1 --force')
114
150
self.assertNotContainsRe(out, 'initial commit')
115
151
self.assertContainsRe(out, 'second commit')
116
self.assertEqual('a1', wt.last_revision())
152
self.assertEqual(['a1'], wt.get_parent_ids())
117
153
self.assertEqual('a1', wt.branch.last_revision())
119
155
def test_uncommit_neg_1(self):
120
156
wt = self.create_simple_tree()
122
out, err = self.run_bzr('uncommit', '-r', '-1', retcode=1)
158
out, err = self.run_bzr('uncommit -r -1', retcode=1)
123
159
self.assertEqual('No revisions to uncommit.\n', out)
125
161
def test_uncommit_merges(self):
130
166
tree2.commit('unchanged', rev_id='b3')
131
167
tree2.commit('unchanged', rev_id='b4')
133
wt.branch.fetch(tree2.branch)
134
wt.set_pending_merges(['b4'])
169
wt.merge_from_branch(tree2.branch)
135
170
wt.commit('merge b4', rev_id='a3')
137
self.assertEqual('a3', wt.last_revision())
138
self.assertEqual([], wt.pending_merges())
141
out, err = self.run_bzr('uncommit', '--force')
143
self.assertEqual('a2', wt.last_revision())
144
self.assertEqual(['b4'], wt.pending_merges())
172
self.assertEqual(['a3'], wt.get_parent_ids())
175
out, err = self.run_bzr('uncommit --force')
177
self.assertEqual(['a2', 'b4'], wt.get_parent_ids())
179
def test_uncommit_pending_merge(self):
180
wt = self.create_simple_tree()
181
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
182
tree2.commit('unchanged', rev_id='b3')
184
wt.branch.fetch(tree2.branch)
185
wt.set_pending_merges(['b3'])
188
out, err = self.run_bzr('uncommit --force')
189
self.assertEqual(['a1', 'b3'], wt.get_parent_ids())
146
191
def test_uncommit_multiple_merge(self):
147
192
wt = self.create_simple_tree()
149
194
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
151
tree2.commit('unchanged', rev_id='b3')
195
tree2.commit('unchanged', rev_id='b3')
197
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
198
tree3.commit('unchanged', rev_id='c3')
200
wt.merge_from_branch(tree2.branch)
201
wt.commit('merge b3', rev_id='a3')
203
wt.merge_from_branch(tree3.branch)
204
wt.commit('merge c3', rev_id='a4')
206
self.assertEqual(['a4'], wt.get_parent_ids())
209
out, err = self.run_bzr('uncommit --force -r 2')
211
self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
213
def test_uncommit_merge_plus_pending(self):
214
wt = self.create_simple_tree()
216
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
217
tree2.commit('unchanged', rev_id='b3')
218
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
219
tree3.commit('unchanged', rev_id='c3')
152
221
wt.branch.fetch(tree2.branch)
153
222
wt.set_pending_merges(['b3'])
154
223
wt.commit('merge b3', rev_id='a3')
156
tree2.commit('unchanged', rev_id='b4')
157
wt.branch.fetch(tree2.branch)
158
wt.set_pending_merges(['b4'])
159
wt.commit('merge b4', rev_id='a4')
161
self.assertEqual('a4', wt.last_revision())
162
self.assertEqual([], wt.pending_merges())
226
wt.merge_from_branch(tree3.branch)
228
self.assertEqual(['a3', 'c3'], wt.get_parent_ids())
165
out, err = self.run_bzr('uncommit', '--force', '-r', '2')
167
self.assertEqual('a2', wt.last_revision())
168
self.assertEqual(['b3', 'b4'], wt.pending_merges())
231
out, err = self.run_bzr('uncommit --force -r 2')
233
self.assertEqual(['a2', 'b3', 'c3'], wt.get_parent_ids())
235
def test_uncommit_shows_log_with_revision_id(self):
236
wt = self.create_simple_tree()
238
script = ScriptRunner()
239
script.run_script(self, """
241
$ bzr uncommit --force
245
The above revision(s) will be removed.
246
You can restore the old tip by running:
247
bzr pull . -r revid:a2
170
250
def test_uncommit_octopus_merge(self):
171
251
# Check that uncommit keeps the pending merges in the same order
252
# though it will also filter out ones in the ancestry
172
253
wt = self.create_simple_tree()
174
255
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
177
258
tree2.commit('unchanged', rev_id='b3')
178
259
tree3.commit('unchanged', rev_id='c3')
179
wt.branch.fetch(tree2.branch)
180
wt.branch.fetch(tree3.branch)
181
wt.set_pending_merges(['b3', 'c3'])
261
wt.merge_from_branch(tree2.branch)
262
wt.merge_from_branch(tree3.branch, force=True)
182
263
wt.commit('merge b3, c3', rev_id='a3')
184
265
tree2.commit('unchanged', rev_id='b4')
185
266
tree3.commit('unchanged', rev_id='c4')
186
wt.branch.fetch(tree2.branch)
187
wt.branch.fetch(tree3.branch)
188
wt.set_pending_merges(['c4', 'b4'])
268
wt.merge_from_branch(tree3.branch)
269
wt.merge_from_branch(tree2.branch, force=True)
189
270
wt.commit('merge b4, c4', rev_id='a4')
191
self.assertEqual('a4', wt.last_revision())
192
self.assertEqual([], wt.pending_merges())
272
self.assertEqual(['a4'], wt.get_parent_ids())
195
out, err = self.run_bzr('uncommit', '--force', '-r', '2')
197
self.assertEqual('a2', wt.last_revision())
198
self.assertEqual(['b3', 'c3', 'c4', 'b4'], wt.pending_merges())
275
out, err = self.run_bzr('uncommit --force -r 2')
277
self.assertEqual(['a2', 'c4', 'b4'], wt.get_parent_ids())
279
def test_uncommit_nonascii(self):
280
tree = self.make_branch_and_tree('tree')
281
tree.commit(u'\u1234 message')
282
out, err = self.run_bzr('uncommit --force tree', encoding='ascii')
283
self.assertContainsRe(out, r'\? message')
285
def test_uncommit_removes_tags(self):
286
tree = self.make_branch_and_tree('tree')
287
revid = tree.commit('message')
288
tree.branch.tags.set_tag("atag", revid)
289
out, err = self.run_bzr('uncommit --force tree')
290
self.assertEquals({}, tree.branch.tags.get_tag_dict())
292
def test_uncommit_keep_tags(self):
293
tree = self.make_branch_and_tree('tree')
294
revid = tree.commit('message')
295
tree.branch.tags.set_tag("atag", revid)
296
out, err = self.run_bzr('uncommit --keep-tags --force tree')
297
self.assertEquals({"atag": revid}, tree.branch.tags.get_tag_dict())
300
class TestSmartServerUncommit(TestCaseWithTransport):
302
def test_uncommit(self):
303
self.setup_smart_server_with_call_log()
304
t = self.make_branch_and_tree('from')
305
for count in range(2):
306
t.commit(message='commit %d' % count)
307
self.reset_smart_call_log()
308
out, err = self.run_bzr(['uncommit', '--force', self.get_url('from')])
309
# This figure represent the amount of work to perform this use case. It
310
# is entirely ok to reduce this number if a test fails due to rpc_count
311
# being too low. If rpc_count increases, more network roundtrips have
312
# become necessary for this use case. Please do not adjust this number
313
# upwards without agreement from bzr's network support maintainers.
314
self.assertLength(14, self.hpss_calls)
315
self.assertLength(1, self.hpss_connections)
316
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)