Merge lp:~mhammond/bzr/update-r into lp:~bzr/bzr/trunk-old
- update-r
- Merge into trunk-old
Proposed by
John A Meinel
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Martin Pool | ||||
Proposed branch: | lp:~mhammond/bzr/update-r | ||||
Merge into: | lp:~bzr/bzr/trunk-old | ||||
Diff against target: |
332 lines (has conflicts)
Text conflict in NEWS Text conflict in bzrlib/builtins.py |
||||
To merge this branch: | bzr merge lp:~mhammond/bzr/update-r | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John A Meinel | Needs Information | ||
Review via email: mp+6980@code.launchpad.net |
This proposal supersedes a proposal from 2008-08-24.
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote : | # |
It would be good to merge this feature.
One comment - the code in cmd_update is getting large enough that it might be good to move it to a non-cli-specific place, so that method does not get too large and so that it can be reused by other interfaces.
Revision history for this message
Martin Pool (mbp) wrote : | # |
I'll close this patch, but work on it myself as (kinda) patch pilot.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-08-30 23:51:10 +0000 | |||
3 | +++ NEWS 2009-08-31 04:37:51 +0000 | |||
4 | @@ -1,5 +1,6 @@ | |||
5 | 1 | #################### | 1 | #################### |
6 | 2 | Bazaar Release Notes | 2 | Bazaar Release Notes |
7 | 3 | <<<<<<< TREE | ||
8 | 3 | #################### | 4 | #################### |
9 | 4 | 5 | ||
10 | 5 | 6 | ||
11 | @@ -3486,6 +3487,68 @@ | |||
12 | 3486 | * Fix a regression in knit => pack fetching. We had a logic | 3487 | * Fix a regression in knit => pack fetching. We had a logic |
13 | 3487 | inversion, causing the fetch to insert fulltexts in random order, | 3488 | inversion, causing the fetch to insert fulltexts in random order, |
14 | 3488 | rather than preserving deltas. (John Arbash Meinel, #256757) | 3489 | rather than preserving deltas. (John Arbash Meinel, #256757) |
15 | 3490 | ======= | ||
16 | 3491 | -------------------- | ||
17 | 3492 | |||
18 | 3493 | .. contents:: | ||
19 | 3494 | |||
20 | 3495 | IN DEVELOPMENT | ||
21 | 3496 | -------------- | ||
22 | 3497 | |||
23 | 3498 | CHANGES: | ||
24 | 3499 | |||
25 | 3500 | * ``bzr export`` can now export a subdirectory of a project. | ||
26 | 3501 | (Robert Collins) | ||
27 | 3502 | |||
28 | 3503 | * ``bzr rm`` will now scan for files that are missing and remove just | ||
29 | 3504 | them automatically, much as ``bzr add`` scans for new files that | ||
30 | 3505 | are not ignored and adds them automatically. (Robert Collins) | ||
31 | 3506 | |||
32 | 3507 | IMPROVEMENTS: | ||
33 | 3508 | |||
34 | 3509 | * ``bzr init`` and ``bzr init-repo`` will now print out the same as | ||
35 | 3510 | ``bzr info`` if it completed successfully. | ||
36 | 3511 | (Marius Kruger) | ||
37 | 3512 | |||
38 | 3513 | * ``bzr uncommit`` logs the old tip revision id, and displays how to | ||
39 | 3514 | restore the branch to that tip using ``bzr pull``. This allows you | ||
40 | 3515 | to recover if you realize you uncommitted the wrong thing. | ||
41 | 3516 | (John Arbash Meinel) | ||
42 | 3517 | |||
43 | 3518 | * ``bzr update`` now takes a ``--revision`` argument. This lets you | ||
44 | 3519 | change the revision of the working tree to any revision in the | ||
45 | 3520 | ancestry of the current or master branch. (Matthieu Moy, #45719) | ||
46 | 3521 | |||
47 | 3522 | BUG FIXES: | ||
48 | 3523 | |||
49 | 3524 | * ``bzr rm`` is now aliased to ``bzr del`` for the convenience of svn | ||
50 | 3525 | users. (Robert Collins, #205416) | ||
51 | 3526 | |||
52 | 3527 | * ``WorkingTree4`` trees will now correctly report missing-and-new | ||
53 | 3528 | paths in the output of ``iter_changes``. (Robert Collins) | ||
54 | 3529 | |||
55 | 3530 | API CHANGES: | ||
56 | 3531 | |||
57 | 3532 | * Exporters now take 4 parameters. (Robert Collins) | ||
58 | 3533 | |||
59 | 3534 | * ``Tree.iter_changes`` will now return False for the content change | ||
60 | 3535 | field when a file is missing in the basis tree and not present in | ||
61 | 3536 | the target tree. Previously it returned True unconditionally. | ||
62 | 3537 | (Robert Collins) | ||
63 | 3538 | |||
64 | 3539 | TESTING: | ||
65 | 3540 | |||
66 | 3541 | * ``addCleanup`` now takes ``*arguments`` and ``**keyword_arguments`` | ||
67 | 3542 | which are then passed to the cleanup callable as it is run. In | ||
68 | 3543 | addition, addCleanup no longer requires that the callables passed to | ||
69 | 3544 | it be unique. (Jonathan Lange) | ||
70 | 3545 | |||
71 | 3546 | INTERNALS: | ||
72 | 3547 | |||
73 | 3548 | * ``bzrlib.diff.DiffTree.show_diff`` now skips changes where the kind | ||
74 | 3549 | is unknown in both source and target. | ||
75 | 3550 | (Robert Collins, Aaron Bentley) | ||
76 | 3551 | >>>>>>> MERGE-SOURCE | ||
77 | 3489 | 3552 | ||
78 | 3490 | 3553 | ||
79 | 3491 | bzr 1.6rc3 2008-08-14 | 3554 | bzr 1.6rc3 2008-08-14 |
80 | 3492 | 3555 | ||
81 | === modified file 'bzrlib/builtins.py' | |||
82 | --- bzrlib/builtins.py 2009-08-28 05:00:33 +0000 | |||
83 | +++ bzrlib/builtins.py 2009-08-31 04:37:52 +0000 | |||
84 | @@ -1385,12 +1385,17 @@ | |||
85 | 1385 | 1385 | ||
86 | 1386 | _see_also = ['pull', 'working-trees', 'status-flags'] | 1386 | _see_also = ['pull', 'working-trees', 'status-flags'] |
87 | 1387 | takes_args = ['dir?'] | 1387 | takes_args = ['dir?'] |
88 | 1388 | takes_options = ['revision'] | ||
89 | 1388 | aliases = ['up'] | 1389 | aliases = ['up'] |
90 | 1389 | 1390 | ||
92 | 1390 | def run(self, dir='.'): | 1391 | def run(self, dir='.', revision=None): |
93 | 1392 | if revision is not None and len(revision) != 1: | ||
94 | 1393 | raise errors.BzrCommandError( | ||
95 | 1394 | "bzr update --revision takes exactly one revision") | ||
96 | 1391 | tree = WorkingTree.open_containing(dir)[0] | 1395 | tree = WorkingTree.open_containing(dir)[0] |
97 | 1396 | branch = tree.branch | ||
98 | 1392 | possible_transports = [] | 1397 | possible_transports = [] |
100 | 1393 | master = tree.branch.get_master_branch( | 1398 | master = branch.get_master_branch( |
101 | 1394 | possible_transports=possible_transports) | 1399 | possible_transports=possible_transports) |
102 | 1395 | if master is not None: | 1400 | if master is not None: |
103 | 1396 | tree.lock_write() | 1401 | tree.lock_write() |
104 | @@ -1398,6 +1403,7 @@ | |||
105 | 1398 | tree.lock_tree_write() | 1403 | tree.lock_tree_write() |
106 | 1399 | try: | 1404 | try: |
107 | 1400 | existing_pending_merges = tree.get_parent_ids()[1:] | 1405 | existing_pending_merges = tree.get_parent_ids()[1:] |
108 | 1406 | <<<<<<< TREE | ||
109 | 1401 | last_rev = _mod_revision.ensure_null(tree.last_revision()) | 1407 | last_rev = _mod_revision.ensure_null(tree.last_revision()) |
110 | 1402 | if last_rev == _mod_revision.ensure_null( | 1408 | if last_rev == _mod_revision.ensure_null( |
111 | 1403 | tree.branch.last_revision()): | 1409 | tree.branch.last_revision()): |
112 | @@ -1413,6 +1419,44 @@ | |||
113 | 1413 | view_info=view_info), possible_transports=possible_transports) | 1419 | view_info=view_info), possible_transports=possible_transports) |
114 | 1414 | revno = tree.branch.revision_id_to_revno( | 1420 | revno = tree.branch.revision_id_to_revno( |
115 | 1415 | _mod_revision.ensure_null(tree.last_revision())) | 1421 | _mod_revision.ensure_null(tree.last_revision())) |
116 | 1422 | ======= | ||
117 | 1423 | # potentially get new revisions from the master branch. | ||
118 | 1424 | # needed for the case where -r N is given, with N not yet | ||
119 | 1425 | # in the local branch for a heavyweight checkout. | ||
120 | 1426 | if revision is not None: | ||
121 | 1427 | try: | ||
122 | 1428 | rev = revision[0].in_history(branch).rev_id | ||
123 | 1429 | # no need to run branch.update() | ||
124 | 1430 | old_tip = None | ||
125 | 1431 | except (errors.NoSuchRevision, errors.InvalidRevisionSpec): | ||
126 | 1432 | # revision was not there, but is maybe in the master. | ||
127 | 1433 | old_tip = branch.update(possible_transports) | ||
128 | 1434 | rev = revision[0].in_history(branch).rev_id | ||
129 | 1435 | else: | ||
130 | 1436 | if master is None: | ||
131 | 1437 | old_tip = None | ||
132 | 1438 | else: | ||
133 | 1439 | old_tip = branch.update(possible_transports) | ||
134 | 1440 | rev = branch.last_revision() | ||
135 | 1441 | if rev == _mod_revision.ensure_null(tree.last_revision()): | ||
136 | 1442 | revno = branch.revision_id_to_revno(rev) | ||
137 | 1443 | note("Tree is up to date at revision %d." % (revno,)) | ||
138 | 1444 | return 0 | ||
139 | 1445 | try: | ||
140 | 1446 | conflicts = tree.update( | ||
141 | 1447 | delta._ChangeReporter(unversioned_filter=tree.is_ignored), | ||
142 | 1448 | possible_transports=possible_transports, | ||
143 | 1449 | revision=rev, | ||
144 | 1450 | old_tip=old_tip) | ||
145 | 1451 | except errors.NoSuchRevision, e: | ||
146 | 1452 | raise errors.BzrCommandError( | ||
147 | 1453 | "branch has no revision %s\n" | ||
148 | 1454 | "bzr update --revision only works" | ||
149 | 1455 | " for a revision in the branch history" | ||
150 | 1456 | % (e.revision)) | ||
151 | 1457 | revno = branch.revision_id_to_revno( | ||
152 | 1458 | _mod_revision.ensure_null(rev)) | ||
153 | 1459 | >>>>>>> MERGE-SOURCE | ||
154 | 1416 | note('Updated to revision %d.' % (revno,)) | 1460 | note('Updated to revision %d.' % (revno,)) |
155 | 1417 | if tree.get_parent_ids()[1:] != existing_pending_merges: | 1461 | if tree.get_parent_ids()[1:] != existing_pending_merges: |
156 | 1418 | note('Your local commits will now show as pending merges with ' | 1462 | note('Your local commits will now show as pending merges with ' |
157 | 1419 | 1463 | ||
158 | === modified file 'bzrlib/tests/blackbox/test_update.py' | |||
159 | --- bzrlib/tests/blackbox/test_update.py 2009-03-23 14:59:43 +0000 | |||
160 | +++ bzrlib/tests/blackbox/test_update.py 2009-08-31 04:37:52 +0000 | |||
161 | @@ -209,3 +209,74 @@ | |||
162 | 209 | lightweight=True) | 209 | lightweight=True) |
163 | 210 | tree.commit('empty commit') | 210 | tree.commit('empty commit') |
164 | 211 | self.run_bzr('update checkout') | 211 | self.run_bzr('update checkout') |
165 | 212 | |||
166 | 213 | def test_update_dash_r(self): | ||
167 | 214 | # Test that 'bzr update' works correctly when you have | ||
168 | 215 | # an update in the master tree, and a lightweight checkout | ||
169 | 216 | # which has merged another branch | ||
170 | 217 | master = self.make_branch_and_tree('master') | ||
171 | 218 | os.chdir('master') | ||
172 | 219 | self.build_tree(['./file1']) | ||
173 | 220 | master.add(['file1']) | ||
174 | 221 | master.commit('one', rev_id='m1') | ||
175 | 222 | self.build_tree(['./file2']) | ||
176 | 223 | master.add(['file2']) | ||
177 | 224 | master.commit('two', rev_id='m2') | ||
178 | 225 | |||
179 | 226 | out, err = self.run_bzr('update -r 1') | ||
180 | 227 | self.assertEqual('', out) | ||
181 | 228 | self.assertEqual('-D file2\nAll changes applied successfully.\n' | ||
182 | 229 | 'Updated to revision 1.\n', err) | ||
183 | 230 | self.failUnlessExists('./file1') | ||
184 | 231 | self.failIfExists('./file2') | ||
185 | 232 | # hrm - the below doesn't look correct for all formats... | ||
186 | 233 | #self.check_file_contents('.bzr/checkout/last-revision', | ||
187 | 234 | # 'm1') | ||
188 | 235 | |||
189 | 236 | def test_update_dash_r_outside_history(self): | ||
190 | 237 | # Test that 'bzr update' works correctly when you have | ||
191 | 238 | # an update in the master tree, and a lightweight checkout | ||
192 | 239 | # which has merged another branch | ||
193 | 240 | master = self.make_branch_and_tree('master') | ||
194 | 241 | self.build_tree(['master/file1']) | ||
195 | 242 | master.add(['file1']) | ||
196 | 243 | master.commit('one', rev_id='m1') | ||
197 | 244 | |||
198 | 245 | # Create a second branch, with an extra commit | ||
199 | 246 | other = master.bzrdir.sprout('other').open_workingtree() | ||
200 | 247 | self.build_tree(['other/file2']) | ||
201 | 248 | other.add(['file2']) | ||
202 | 249 | other.commit('other2', rev_id='o2') | ||
203 | 250 | |||
204 | 251 | os.chdir('master') | ||
205 | 252 | self.run_bzr('merge ../other') | ||
206 | 253 | master.commit('merge', rev_id='merge') | ||
207 | 254 | |||
208 | 255 | out, err = self.run_bzr('update -r revid:o2', | ||
209 | 256 | retcode=3) | ||
210 | 257 | self.assertEqual('', out) | ||
211 | 258 | self.assertEqual('bzr: ERROR: branch has no revision o2\n' | ||
212 | 259 | 'bzr update --revision only works' | ||
213 | 260 | ' for a revision in the branch history\n', | ||
214 | 261 | err) | ||
215 | 262 | |||
216 | 263 | def test_update_dash_r_in_master(self): | ||
217 | 264 | # Test that 'bzr update' works correctly when you have | ||
218 | 265 | # an update in the master tree, | ||
219 | 266 | master = self.make_branch_and_tree('master') | ||
220 | 267 | self.build_tree(['master/file1']) | ||
221 | 268 | master.add(['file1']) | ||
222 | 269 | master.commit('one', rev_id='m1') | ||
223 | 270 | |||
224 | 271 | self.run_bzr('checkout master checkout') | ||
225 | 272 | |||
226 | 273 | # add a revision in the master. | ||
227 | 274 | self.build_tree(['master/file2']) | ||
228 | 275 | master.add(['file2']) | ||
229 | 276 | master.commit('two', rev_id='m2') | ||
230 | 277 | |||
231 | 278 | os.chdir('checkout') | ||
232 | 279 | out, err = self.run_bzr('update -r revid:m2') | ||
233 | 280 | self.assertEqual('', out) | ||
234 | 281 | self.assertEqual('+N file2\nAll changes applied successfully.\n' | ||
235 | 282 | 'Updated to revision 2.\n', err) | ||
236 | 212 | 283 | ||
237 | === modified file 'bzrlib/workingtree.py' | |||
238 | --- bzrlib/workingtree.py 2009-08-26 05:38:16 +0000 | |||
239 | +++ bzrlib/workingtree.py 2009-08-31 04:37:52 +0000 | |||
240 | @@ -2177,7 +2177,10 @@ | |||
241 | 2177 | """ | 2177 | """ |
242 | 2178 | raise NotImplementedError(self.unlock) | 2178 | raise NotImplementedError(self.unlock) |
243 | 2179 | 2179 | ||
245 | 2180 | def update(self, change_reporter=None, possible_transports=None): | 2180 | _marker = object() |
246 | 2181 | |||
247 | 2182 | def update(self, change_reporter=None, possible_transports=None, | ||
248 | 2183 | revision=None, old_tip=_marker): | ||
249 | 2181 | """Update a working tree along its branch. | 2184 | """Update a working tree along its branch. |
250 | 2182 | 2185 | ||
251 | 2183 | This will update the branch if its bound too, which means we have | 2186 | This will update the branch if its bound too, which means we have |
252 | @@ -2201,10 +2204,16 @@ | |||
253 | 2201 | - Merge current state -> basis tree of the master w.r.t. the old tree | 2204 | - Merge current state -> basis tree of the master w.r.t. the old tree |
254 | 2202 | basis. | 2205 | basis. |
255 | 2203 | - Do a 'normal' merge of the old branch basis if it is relevant. | 2206 | - Do a 'normal' merge of the old branch basis if it is relevant. |
256 | 2207 | |||
257 | 2208 | :param revision: The target revision to update to. Must be in the | ||
258 | 2209 | revision history. | ||
259 | 2210 | :param old_tip: If branch.update() has already been run, the value it | ||
260 | 2211 | returned (old tip of the branch or None). _marker is used | ||
261 | 2212 | otherwise. | ||
262 | 2204 | """ | 2213 | """ |
263 | 2205 | if self.branch.get_bound_location() is not None: | 2214 | if self.branch.get_bound_location() is not None: |
264 | 2206 | self.lock_write() | 2215 | self.lock_write() |
266 | 2207 | update_branch = True | 2216 | update_branch = (old_tip is self._marker) |
267 | 2208 | else: | 2217 | else: |
268 | 2209 | self.lock_tree_write() | 2218 | self.lock_tree_write() |
269 | 2210 | update_branch = False | 2219 | update_branch = False |
270 | @@ -2212,13 +2221,14 @@ | |||
271 | 2212 | if update_branch: | 2221 | if update_branch: |
272 | 2213 | old_tip = self.branch.update(possible_transports) | 2222 | old_tip = self.branch.update(possible_transports) |
273 | 2214 | else: | 2223 | else: |
276 | 2215 | old_tip = None | 2224 | if old_tip is self._marker: |
277 | 2216 | return self._update_tree(old_tip, change_reporter) | 2225 | old_tip = None |
278 | 2226 | return self._update_tree(old_tip, change_reporter, revision) | ||
279 | 2217 | finally: | 2227 | finally: |
280 | 2218 | self.unlock() | 2228 | self.unlock() |
281 | 2219 | 2229 | ||
282 | 2220 | @needs_tree_write_lock | 2230 | @needs_tree_write_lock |
284 | 2221 | def _update_tree(self, old_tip=None, change_reporter=None): | 2231 | def _update_tree(self, old_tip=None, change_reporter=None, revision=None): |
285 | 2222 | """Update a tree to the master branch. | 2232 | """Update a tree to the master branch. |
286 | 2223 | 2233 | ||
287 | 2224 | :param old_tip: if supplied, the previous tip revision the branch, | 2234 | :param old_tip: if supplied, the previous tip revision the branch, |
288 | @@ -2239,12 +2249,17 @@ | |||
289 | 2239 | last_rev = self.get_parent_ids()[0] | 2249 | last_rev = self.get_parent_ids()[0] |
290 | 2240 | except IndexError: | 2250 | except IndexError: |
291 | 2241 | last_rev = _mod_revision.NULL_REVISION | 2251 | last_rev = _mod_revision.NULL_REVISION |
294 | 2242 | if last_rev != _mod_revision.ensure_null(self.branch.last_revision()): | 2252 | if revision is None: |
295 | 2243 | # merge tree state up to new branch tip. | 2253 | revision = self.branch.last_revision() |
296 | 2254 | else: | ||
297 | 2255 | if revision not in self.branch.revision_history(): | ||
298 | 2256 | raise errors.NoSuchRevision(self.branch, revision) | ||
299 | 2257 | if last_rev != _mod_revision.ensure_null(revision): | ||
300 | 2258 | # merge tree state up to specified revision. | ||
301 | 2244 | basis = self.basis_tree() | 2259 | basis = self.basis_tree() |
302 | 2245 | basis.lock_read() | 2260 | basis.lock_read() |
303 | 2246 | try: | 2261 | try: |
305 | 2247 | to_tree = self.branch.basis_tree() | 2262 | to_tree = self.branch.repository.revision_tree(revision) |
306 | 2248 | if basis.inventory.root is None: | 2263 | if basis.inventory.root is None: |
307 | 2249 | self.set_root_id(to_tree.get_root_id()) | 2264 | self.set_root_id(to_tree.get_root_id()) |
308 | 2250 | self.flush() | 2265 | self.flush() |
309 | @@ -2254,11 +2269,12 @@ | |||
310 | 2254 | basis, | 2269 | basis, |
311 | 2255 | this_tree=self, | 2270 | this_tree=self, |
312 | 2256 | change_reporter=change_reporter) | 2271 | change_reporter=change_reporter) |
313 | 2272 | self.set_last_revision(revision) | ||
314 | 2257 | finally: | 2273 | finally: |
315 | 2258 | basis.unlock() | 2274 | basis.unlock() |
316 | 2259 | # TODO - dedup parents list with things merged by pull ? | 2275 | # TODO - dedup parents list with things merged by pull ? |
317 | 2260 | # reuse the tree we've updated to to set the basis: | 2276 | # reuse the tree we've updated to to set the basis: |
319 | 2261 | parent_trees = [(self.branch.last_revision(), to_tree)] | 2277 | parent_trees = [(revision, to_tree)] |
320 | 2262 | merges = self.get_parent_ids()[1:] | 2278 | merges = self.get_parent_ids()[1:] |
321 | 2263 | # Ideally we ask the tree for the trees here, that way the working | 2279 | # Ideally we ask the tree for the trees here, that way the working |
322 | 2264 | # tree can decide whether to give us the entire tree or give us a | 2280 | # tree can decide whether to give us the entire tree or give us a |
323 | @@ -2294,8 +2310,7 @@ | |||
324 | 2294 | # should be able to remove this extra flush. | 2310 | # should be able to remove this extra flush. |
325 | 2295 | self.flush() | 2311 | self.flush() |
326 | 2296 | graph = self.branch.repository.get_graph() | 2312 | graph = self.branch.repository.get_graph() |
329 | 2297 | base_rev_id = graph.find_unique_lca(self.branch.last_revision(), | 2313 | base_rev_id = graph.find_unique_lca(revision, old_tip) |
328 | 2298 | old_tip) | ||
330 | 2299 | base_tree = self.branch.repository.revision_tree(base_rev_id) | 2314 | base_tree = self.branch.repository.revision_tree(base_rev_id) |
331 | 2300 | other_tree = self.branch.repository.revision_tree(old_tip) | 2315 | other_tree = self.branch.repository.revision_tree(old_tip) |
332 | 2301 | result += merge.merge_inner( | 2316 | result += merge.merge_inner( |
I just resubmitted this, because I wanted it to generate a diff that I could then look over to actually do a review.