Merge lp:~mbp/bzr/45719-update-r into lp:bzr
- 45719-update-r
- Merge into bzr.dev
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | John A Meinel | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~mbp/bzr/45719-update-r | ||||
Merge into: | lp:bzr | ||||
Diff against target: |
361 lines (+176/-37) 5 files modified
NEWS (+13/-5) bzrlib/builtins.py (+41/-15) bzrlib/tests/blackbox/test_update.py (+82/-6) bzrlib/tests/per_workingtree/test_workingtree.py (+14/-0) bzrlib/workingtree.py (+26/-11) |
||||
To merge this branch: | bzr merge lp:~mbp/bzr/45719-update-r | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John A Meinel | Approve | ||
Review via email: mp+16476@code.launchpad.net |
Commit message
Description of the change
Martin Pool (mbp) wrote : | # |
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Martin Pool wrote:
> Martin Pool has proposed merging lp:~mbp/bzr/45719-update-r into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
> Related bugs:
> #45719 update command cannot take a revision number
> https:/
>
>
> This adds a 'bzr update -r' option. If the branch is bound to another, it always updates from the master branch first. This updated form of the patch addresses John's review comments in <https:/
>
> This is an updated version of what was one of the longest-standing merges, and it's for a five-digit bug too <https:/
>
v- does 'change_reporter' need to be inside the try/except ? I think it
just got refactored into there.
+ try:
+ change_reporter = delta._
+ unversioned_
+ view_info=
+ conflicts = tree.update(
+ change_reporter,
+ possible_
+ revision=rev,
+ old_tip=old_tip)
+ except errors.
+ raise errors.
+ "branch has no revision %s\n"
+ "bzr update --revision only works"
+ " for a revision in the branch
history"
+ % (e.revision))
+ if revision is not None:
+ rev = revision[
+ else:
+ rev = branch.
^- It might be clearer as 'rev_id' or 'revision_id = '. We have some
ambiguous naming (is it a Revision, revision_id, or RevSpec...) However,
this is really minor.
review: approve
merge: approve
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAks
sdIAn3ABG2q0Rkq
=IrMf
-----END PGP SIGNATURE-----
Preview Diff
1 | === modified file 'NEWS' |
2 | --- NEWS 2009-12-23 05:04:12 +0000 |
3 | +++ NEWS 2009-12-23 06:33:19 +0000 |
4 | @@ -23,11 +23,10 @@ |
5 | ``locations.conf`` or ``branch.conf``. |
6 | (Ted Gould, Matthew Fuller, Vincent Ladeuil) |
7 | |
8 | -* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies |
9 | - running all tests in the current module, once against a pure python |
10 | - implementation, and once against an extension (pyrex/C) implementation. |
11 | - It can be used to dramatically simplify the implementation of |
12 | - ``load_tests``. (John Arbash Meinel) |
13 | +* ``bzr update`` now takes a ``--revision`` argument. This lets you |
14 | + change the revision of the working tree to any revision in the |
15 | + ancestry of the current or master branch. (Matthieu Moy, Mark Hammond, |
16 | + Martin Pool, #45719) |
17 | |
18 | Bug Fixes |
19 | ********* |
20 | @@ -60,6 +59,9 @@ |
21 | CamelCase. For the features that were more likely to be used, we added a |
22 | deprecation thunk, but not all. (John Arbash Meinel) |
23 | |
24 | +* ``WorkingTree.update`` implementations must now accept a ``revision`` |
25 | + parameter. |
26 | + |
27 | Internals |
28 | ********* |
29 | |
30 | @@ -71,6 +73,12 @@ |
31 | Testing |
32 | ******* |
33 | |
34 | +* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies |
35 | + running all tests in the current module, once against a pure python |
36 | + implementation, and once against an extension (pyrex/C) implementation. |
37 | + It can be used to dramatically simplify the implementation of |
38 | + ``load_tests``. (John Arbash Meinel) |
39 | + |
40 | * ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``. |
41 | This permits features in testtools such as getUniqueInteger and |
42 | getUniqueString to be used. Because of this, testtools version 0.9.2 or |
43 | |
44 | === modified file 'bzrlib/builtins.py' |
45 | --- bzrlib/builtins.py 2009-12-21 17:24:22 +0000 |
46 | +++ bzrlib/builtins.py 2009-12-23 06:33:19 +0000 |
47 | @@ -1388,16 +1388,24 @@ |
48 | |
49 | If you want to discard your local changes, you can just do a |
50 | 'bzr revert' instead of 'bzr commit' after the update. |
51 | + |
52 | + If the tree's branch is bound to a master branch, it will also update |
53 | + the branch from the master. |
54 | """ |
55 | |
56 | _see_also = ['pull', 'working-trees', 'status-flags'] |
57 | takes_args = ['dir?'] |
58 | + takes_options = ['revision'] |
59 | aliases = ['up'] |
60 | |
61 | - def run(self, dir='.'): |
62 | + def run(self, dir='.', revision=None): |
63 | + if revision is not None and len(revision) != 1: |
64 | + raise errors.BzrCommandError( |
65 | + "bzr update --revision takes exactly one revision") |
66 | tree = WorkingTree.open_containing(dir)[0] |
67 | + branch = tree.branch |
68 | possible_transports = [] |
69 | - master = tree.branch.get_master_branch( |
70 | + master = branch.get_master_branch( |
71 | possible_transports=possible_transports) |
72 | if master is not None: |
73 | tree.lock_write() |
74 | @@ -1410,20 +1418,38 @@ |
75 | self.outf.encoding) |
76 | try: |
77 | existing_pending_merges = tree.get_parent_ids()[1:] |
78 | - last_rev = _mod_revision.ensure_null(tree.last_revision()) |
79 | - if last_rev == _mod_revision.ensure_null( |
80 | - tree.branch.last_revision()): |
81 | - # may be up to date, check master too. |
82 | - if master is None or last_rev == _mod_revision.ensure_null( |
83 | - master.last_revision()): |
84 | - revno = tree.branch.revision_id_to_revno(last_rev) |
85 | - note('Tree is up to date at revision %d of branch %s' |
86 | - % (revno, branch_location)) |
87 | - return 0 |
88 | + if master is None: |
89 | + old_tip = None |
90 | + else: |
91 | + # may need to fetch data into a heavyweight checkout |
92 | + # XXX: this may take some time, maybe we should display a |
93 | + # message |
94 | + old_tip = branch.update(possible_transports) |
95 | + if revision is not None: |
96 | + revision_id = revision[0].as_revision_id(branch) |
97 | + else: |
98 | + revision_id = branch.last_revision() |
99 | + if revision_id == _mod_revision.ensure_null(tree.last_revision()): |
100 | + revno = branch.revision_id_to_revno(revision_id) |
101 | + note("Tree is up to date at revision %d of branch %s" % |
102 | + (revno, branch_location)) |
103 | + return 0 |
104 | view_info = _get_view_info_for_change_reporter(tree) |
105 | - conflicts = tree.update( |
106 | - delta._ChangeReporter(unversioned_filter=tree.is_ignored, |
107 | - view_info=view_info), possible_transports=possible_transports) |
108 | + change_reporter = delta._ChangeReporter( |
109 | + unversioned_filter=tree.is_ignored, |
110 | + view_info=view_info) |
111 | + try: |
112 | + conflicts = tree.update( |
113 | + change_reporter, |
114 | + possible_transports=possible_transports, |
115 | + revision=revision_id, |
116 | + old_tip=old_tip) |
117 | + except errors.NoSuchRevision, e: |
118 | + raise errors.BzrCommandError( |
119 | + "branch has no revision %s\n" |
120 | + "bzr update --revision only works" |
121 | + " for a revision in the branch history" |
122 | + % (e.revision)) |
123 | revno = tree.branch.revision_id_to_revno( |
124 | _mod_revision.ensure_null(tree.last_revision())) |
125 | note('Updated to revision %d of branch %s' % |
126 | |
127 | === modified file 'bzrlib/tests/blackbox/test_update.py' |
128 | --- bzrlib/tests/blackbox/test_update.py 2009-12-14 15:51:36 +0000 |
129 | +++ bzrlib/tests/blackbox/test_update.py 2009-12-23 06:33:19 +0000 |
130 | @@ -1,4 +1,4 @@ |
131 | -# Copyright (C) 2006 Canonical Ltd |
132 | +# Copyright (C) 2006, 2009 Canonical Ltd |
133 | # -*- coding: utf-8 -*- |
134 | # |
135 | # This program is free software; you can redistribute it and/or modify |
136 | @@ -29,6 +29,7 @@ |
137 | urlutils, |
138 | workingtree, |
139 | ) |
140 | +from bzrlib.tests.script import ScriptRunner |
141 | |
142 | |
143 | class TestUpdate(tests.TestCaseWithTransport): |
144 | @@ -67,11 +68,11 @@ |
145 | def test_update_up_to_date_checkout(self): |
146 | self.make_branch_and_tree('branch') |
147 | self.run_bzr('checkout branch checkout') |
148 | - out, err = self.run_bzr('update checkout') |
149 | - self.assertEqual('Tree is up to date at revision 0 of branch %s\n' |
150 | - % osutils.pathjoin(self.test_dir, 'branch'), |
151 | - err) |
152 | - self.assertEqual('', out) |
153 | + sr = ScriptRunner() |
154 | + sr.run_script(self, ''' |
155 | +$ bzr update checkout |
156 | +2>Tree is up to date at revision 0 of branch .../branch |
157 | +''') |
158 | |
159 | def test_update_out_of_date_standalone_tree(self): |
160 | # FIXME the default format has to change for this to pass |
161 | @@ -239,3 +240,78 @@ |
162 | lightweight=True) |
163 | tree.commit('empty commit') |
164 | self.run_bzr('update checkout') |
165 | + |
166 | + def test_update_dash_r(self): |
167 | + # Test that 'bzr update' works correctly when you have |
168 | + # an update in the master tree, and a lightweight checkout |
169 | + # which has merged another branch |
170 | + master = self.make_branch_and_tree('master') |
171 | + os.chdir('master') |
172 | + self.build_tree(['./file1']) |
173 | + master.add(['file1']) |
174 | + master.commit('one', rev_id='m1') |
175 | + self.build_tree(['./file2']) |
176 | + master.add(['file2']) |
177 | + master.commit('two', rev_id='m2') |
178 | + |
179 | + sr = ScriptRunner() |
180 | + sr.run_script(self, ''' |
181 | +$ bzr update -r 1 |
182 | +2>-D file2 |
183 | +2>All changes applied successfully. |
184 | +2>Updated to revision 1 of .../master |
185 | +''') |
186 | + self.failUnlessExists('./file1') |
187 | + self.failIfExists('./file2') |
188 | + self.assertEquals(['m1'], master.get_parent_ids()) |
189 | + |
190 | + def test_update_dash_r_outside_history(self): |
191 | + # Test that 'bzr update' works correctly when you have |
192 | + # an update in the master tree, and a lightweight checkout |
193 | + # which has merged another branch |
194 | + master = self.make_branch_and_tree('master') |
195 | + self.build_tree(['master/file1']) |
196 | + master.add(['file1']) |
197 | + master.commit('one', rev_id='m1') |
198 | + |
199 | + # Create a second branch, with an extra commit |
200 | + other = master.bzrdir.sprout('other').open_workingtree() |
201 | + self.build_tree(['other/file2']) |
202 | + other.add(['file2']) |
203 | + other.commit('other2', rev_id='o2') |
204 | + |
205 | + os.chdir('master') |
206 | + self.run_bzr('merge ../other') |
207 | + master.commit('merge', rev_id='merge') |
208 | + |
209 | + out, err = self.run_bzr('update -r revid:o2', |
210 | + retcode=3) |
211 | + self.assertEqual('', out) |
212 | + self.assertEqual('bzr: ERROR: branch has no revision o2\n' |
213 | + 'bzr update --revision only works' |
214 | + ' for a revision in the branch history\n', |
215 | + err) |
216 | + |
217 | + def test_update_dash_r_in_master(self): |
218 | + # Test that 'bzr update' works correctly when you have |
219 | + # an update in the master tree, |
220 | + master = self.make_branch_and_tree('master') |
221 | + self.build_tree(['master/file1']) |
222 | + master.add(['file1']) |
223 | + master.commit('one', rev_id='m1') |
224 | + |
225 | + self.run_bzr('checkout master checkout') |
226 | + |
227 | + # add a revision in the master. |
228 | + self.build_tree(['master/file2']) |
229 | + master.add(['file2']) |
230 | + master.commit('two', rev_id='m2') |
231 | + |
232 | + os.chdir('checkout') |
233 | + sr = ScriptRunner() |
234 | + sr.run_script(self, ''' |
235 | +$ bzr update -r revid:m2 |
236 | +2>+N file2 |
237 | +2>All changes applied successfully. |
238 | +2>Updated to revision 2 of branch .../master |
239 | +''') |
240 | |
241 | === modified file 'bzrlib/tests/per_workingtree/test_workingtree.py' |
242 | --- bzrlib/tests/per_workingtree/test_workingtree.py 2009-11-08 05:28:57 +0000 |
243 | +++ bzrlib/tests/per_workingtree/test_workingtree.py 2009-12-23 06:33:19 +0000 |
244 | @@ -590,6 +590,20 @@ |
245 | self.assertEqual(master_tree.branch.revision_history(), |
246 | tree.branch.revision_history()) |
247 | |
248 | + def test_update_takes_revision_parameter(self): |
249 | + wt = self.make_branch_and_tree('wt') |
250 | + self.build_tree_contents([('wt/a', 'old content')]) |
251 | + wt.add(['a']) |
252 | + rev1 = wt.commit('first master commit') |
253 | + self.build_tree_contents([('wt/a', 'new content')]) |
254 | + rev2 = wt.commit('second master commit') |
255 | + # https://bugs.edge.launchpad.net/bzr/+bug/45719/comments/20 |
256 | + # when adding 'update -r' we should make sure all wt formats support |
257 | + # it |
258 | + conflicts = wt.update(revision=rev1) |
259 | + self.assertFileEqual('old content', 'wt/a') |
260 | + self.assertEqual([rev1], wt.get_parent_ids()) |
261 | + |
262 | def test_merge_modified_detects_corruption(self): |
263 | # FIXME: This doesn't really test that it works; also this is not |
264 | # implementation-independent. mbp 20070226 |
265 | |
266 | === modified file 'bzrlib/workingtree.py' |
267 | --- bzrlib/workingtree.py 2009-12-02 18:15:55 +0000 |
268 | +++ bzrlib/workingtree.py 2009-12-23 06:33:19 +0000 |
269 | @@ -2191,7 +2191,10 @@ |
270 | """ |
271 | raise NotImplementedError(self.unlock) |
272 | |
273 | - def update(self, change_reporter=None, possible_transports=None): |
274 | + _marker = object() |
275 | + |
276 | + def update(self, change_reporter=None, possible_transports=None, |
277 | + revision=None, old_tip=_marker): |
278 | """Update a working tree along its branch. |
279 | |
280 | This will update the branch if its bound too, which means we have |
281 | @@ -2215,10 +2218,16 @@ |
282 | - Merge current state -> basis tree of the master w.r.t. the old tree |
283 | basis. |
284 | - Do a 'normal' merge of the old branch basis if it is relevant. |
285 | + |
286 | + :param revision: The target revision to update to. Must be in the |
287 | + revision history. |
288 | + :param old_tip: If branch.update() has already been run, the value it |
289 | + returned (old tip of the branch or None). _marker is used |
290 | + otherwise. |
291 | """ |
292 | if self.branch.get_bound_location() is not None: |
293 | self.lock_write() |
294 | - update_branch = True |
295 | + update_branch = (old_tip is self._marker) |
296 | else: |
297 | self.lock_tree_write() |
298 | update_branch = False |
299 | @@ -2226,13 +2235,14 @@ |
300 | if update_branch: |
301 | old_tip = self.branch.update(possible_transports) |
302 | else: |
303 | - old_tip = None |
304 | - return self._update_tree(old_tip, change_reporter) |
305 | + if old_tip is self._marker: |
306 | + old_tip = None |
307 | + return self._update_tree(old_tip, change_reporter, revision) |
308 | finally: |
309 | self.unlock() |
310 | |
311 | @needs_tree_write_lock |
312 | - def _update_tree(self, old_tip=None, change_reporter=None): |
313 | + def _update_tree(self, old_tip=None, change_reporter=None, revision=None): |
314 | """Update a tree to the master branch. |
315 | |
316 | :param old_tip: if supplied, the previous tip revision the branch, |
317 | @@ -2253,12 +2263,17 @@ |
318 | last_rev = self.get_parent_ids()[0] |
319 | except IndexError: |
320 | last_rev = _mod_revision.NULL_REVISION |
321 | - if last_rev != _mod_revision.ensure_null(self.branch.last_revision()): |
322 | - # merge tree state up to new branch tip. |
323 | + if revision is None: |
324 | + revision = self.branch.last_revision() |
325 | + else: |
326 | + if revision not in self.branch.revision_history(): |
327 | + raise errors.NoSuchRevision(self.branch, revision) |
328 | + if last_rev != _mod_revision.ensure_null(revision): |
329 | + # merge tree state up to specified revision. |
330 | basis = self.basis_tree() |
331 | basis.lock_read() |
332 | try: |
333 | - to_tree = self.branch.basis_tree() |
334 | + to_tree = self.branch.repository.revision_tree(revision) |
335 | if basis.inventory.root is None: |
336 | self.set_root_id(to_tree.get_root_id()) |
337 | self.flush() |
338 | @@ -2268,11 +2283,12 @@ |
339 | basis, |
340 | this_tree=self, |
341 | change_reporter=change_reporter) |
342 | + self.set_last_revision(revision) |
343 | finally: |
344 | basis.unlock() |
345 | # TODO - dedup parents list with things merged by pull ? |
346 | # reuse the tree we've updated to to set the basis: |
347 | - parent_trees = [(self.branch.last_revision(), to_tree)] |
348 | + parent_trees = [(revision, to_tree)] |
349 | merges = self.get_parent_ids()[1:] |
350 | # Ideally we ask the tree for the trees here, that way the working |
351 | # tree can decide whether to give us the entire tree or give us a |
352 | @@ -2308,8 +2324,7 @@ |
353 | # should be able to remove this extra flush. |
354 | self.flush() |
355 | graph = self.branch.repository.get_graph() |
356 | - base_rev_id = graph.find_unique_lca(self.branch.last_revision(), |
357 | - old_tip) |
358 | + base_rev_id = graph.find_unique_lca(revision, old_tip) |
359 | base_tree = self.branch.repository.revision_tree(base_rev_id) |
360 | other_tree = self.branch.repository.revision_tree(old_tip) |
361 | result += merge.merge_inner( |
This adds a 'bzr update -r' option. If the branch is bound to another, it always updates from the master branch first. This updated form of the patch addresses John's review comments in <https:/ /bugs.edge. launchpad. net/bzr/ +bug/45719/ comments/ 20>.
This is an updated version of what was one of the longest-standing merges, and it's for a five-digit bug too <https:/ /bugs.edge. launchpad. net/bzr/ +bug/45719>.