Merge lp:~mhammond/bzr/update-r into lp:~bzr/bzr/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
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.

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

I just resubmitted this, because I wanted it to generate a diff that I could then look over to actually do a review.

review: Needs Information
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 ####################
6 Bazaar Release Notes
7+<<<<<<< TREE
8 ####################
9
10
11@@ -3486,6 +3487,68 @@
12 * Fix a regression in knit => pack fetching. We had a logic
13 inversion, causing the fetch to insert fulltexts in random order,
14 rather than preserving deltas. (John Arbash Meinel, #256757)
15+=======
16+--------------------
17+
18+.. contents::
19+
20+IN DEVELOPMENT
21+--------------
22+
23+ CHANGES:
24+
25+ * ``bzr export`` can now export a subdirectory of a project.
26+ (Robert Collins)
27+
28+ * ``bzr rm`` will now scan for files that are missing and remove just
29+ them automatically, much as ``bzr add`` scans for new files that
30+ are not ignored and adds them automatically. (Robert Collins)
31+
32+ IMPROVEMENTS:
33+
34+ * ``bzr init`` and ``bzr init-repo`` will now print out the same as
35+ ``bzr info`` if it completed successfully.
36+ (Marius Kruger)
37+
38+ * ``bzr uncommit`` logs the old tip revision id, and displays how to
39+ restore the branch to that tip using ``bzr pull``. This allows you
40+ to recover if you realize you uncommitted the wrong thing.
41+ (John Arbash Meinel)
42+
43+ * ``bzr update`` now takes a ``--revision`` argument. This lets you
44+ change the revision of the working tree to any revision in the
45+ ancestry of the current or master branch. (Matthieu Moy, #45719)
46+
47+ BUG FIXES:
48+
49+ * ``bzr rm`` is now aliased to ``bzr del`` for the convenience of svn
50+ users. (Robert Collins, #205416)
51+
52+ * ``WorkingTree4`` trees will now correctly report missing-and-new
53+ paths in the output of ``iter_changes``. (Robert Collins)
54+
55+ API CHANGES:
56+
57+ * Exporters now take 4 parameters. (Robert Collins)
58+
59+ * ``Tree.iter_changes`` will now return False for the content change
60+ field when a file is missing in the basis tree and not present in
61+ the target tree. Previously it returned True unconditionally.
62+ (Robert Collins)
63+
64+ TESTING:
65+
66+ * ``addCleanup`` now takes ``*arguments`` and ``**keyword_arguments``
67+ which are then passed to the cleanup callable as it is run. In
68+ addition, addCleanup no longer requires that the callables passed to
69+ it be unique. (Jonathan Lange)
70+
71+ INTERNALS:
72+
73+ * ``bzrlib.diff.DiffTree.show_diff`` now skips changes where the kind
74+ is unknown in both source and target.
75+ (Robert Collins, Aaron Bentley)
76+>>>>>>> MERGE-SOURCE
77
78
79 bzr 1.6rc3 2008-08-14
80
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
86 _see_also = ['pull', 'working-trees', 'status-flags']
87 takes_args = ['dir?']
88+ takes_options = ['revision']
89 aliases = ['up']
90
91- def run(self, dir='.'):
92+ def run(self, dir='.', revision=None):
93+ if revision is not None and len(revision) != 1:
94+ raise errors.BzrCommandError(
95+ "bzr update --revision takes exactly one revision")
96 tree = WorkingTree.open_containing(dir)[0]
97+ branch = tree.branch
98 possible_transports = []
99- master = tree.branch.get_master_branch(
100+ master = branch.get_master_branch(
101 possible_transports=possible_transports)
102 if master is not None:
103 tree.lock_write()
104@@ -1398,6 +1403,7 @@
105 tree.lock_tree_write()
106 try:
107 existing_pending_merges = tree.get_parent_ids()[1:]
108+<<<<<<< TREE
109 last_rev = _mod_revision.ensure_null(tree.last_revision())
110 if last_rev == _mod_revision.ensure_null(
111 tree.branch.last_revision()):
112@@ -1413,6 +1419,44 @@
113 view_info=view_info), possible_transports=possible_transports)
114 revno = tree.branch.revision_id_to_revno(
115 _mod_revision.ensure_null(tree.last_revision()))
116+=======
117+ # potentially get new revisions from the master branch.
118+ # needed for the case where -r N is given, with N not yet
119+ # in the local branch for a heavyweight checkout.
120+ if revision is not None:
121+ try:
122+ rev = revision[0].in_history(branch).rev_id
123+ # no need to run branch.update()
124+ old_tip = None
125+ except (errors.NoSuchRevision, errors.InvalidRevisionSpec):
126+ # revision was not there, but is maybe in the master.
127+ old_tip = branch.update(possible_transports)
128+ rev = revision[0].in_history(branch).rev_id
129+ else:
130+ if master is None:
131+ old_tip = None
132+ else:
133+ old_tip = branch.update(possible_transports)
134+ rev = branch.last_revision()
135+ if rev == _mod_revision.ensure_null(tree.last_revision()):
136+ revno = branch.revision_id_to_revno(rev)
137+ note("Tree is up to date at revision %d." % (revno,))
138+ return 0
139+ try:
140+ conflicts = tree.update(
141+ delta._ChangeReporter(unversioned_filter=tree.is_ignored),
142+ possible_transports=possible_transports,
143+ revision=rev,
144+ old_tip=old_tip)
145+ except errors.NoSuchRevision, e:
146+ raise errors.BzrCommandError(
147+ "branch has no revision %s\n"
148+ "bzr update --revision only works"
149+ " for a revision in the branch history"
150+ % (e.revision))
151+ revno = branch.revision_id_to_revno(
152+ _mod_revision.ensure_null(rev))
153+>>>>>>> MERGE-SOURCE
154 note('Updated to revision %d.' % (revno,))
155 if tree.get_parent_ids()[1:] != existing_pending_merges:
156 note('Your local commits will now show as pending merges with '
157
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 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+ out, err = self.run_bzr('update -r 1')
180+ self.assertEqual('', out)
181+ self.assertEqual('-D file2\nAll changes applied successfully.\n'
182+ 'Updated to revision 1.\n', err)
183+ self.failUnlessExists('./file1')
184+ self.failIfExists('./file2')
185+ # hrm - the below doesn't look correct for all formats...
186+ #self.check_file_contents('.bzr/checkout/last-revision',
187+ # 'm1')
188+
189+ def test_update_dash_r_outside_history(self):
190+ # Test that 'bzr update' works correctly when you have
191+ # an update in the master tree, and a lightweight checkout
192+ # which has merged another branch
193+ master = self.make_branch_and_tree('master')
194+ self.build_tree(['master/file1'])
195+ master.add(['file1'])
196+ master.commit('one', rev_id='m1')
197+
198+ # Create a second branch, with an extra commit
199+ other = master.bzrdir.sprout('other').open_workingtree()
200+ self.build_tree(['other/file2'])
201+ other.add(['file2'])
202+ other.commit('other2', rev_id='o2')
203+
204+ os.chdir('master')
205+ self.run_bzr('merge ../other')
206+ master.commit('merge', rev_id='merge')
207+
208+ out, err = self.run_bzr('update -r revid:o2',
209+ retcode=3)
210+ self.assertEqual('', out)
211+ self.assertEqual('bzr: ERROR: branch has no revision o2\n'
212+ 'bzr update --revision only works'
213+ ' for a revision in the branch history\n',
214+ err)
215+
216+ def test_update_dash_r_in_master(self):
217+ # Test that 'bzr update' works correctly when you have
218+ # an update in the master tree,
219+ master = self.make_branch_and_tree('master')
220+ self.build_tree(['master/file1'])
221+ master.add(['file1'])
222+ master.commit('one', rev_id='m1')
223+
224+ self.run_bzr('checkout master checkout')
225+
226+ # add a revision in the master.
227+ self.build_tree(['master/file2'])
228+ master.add(['file2'])
229+ master.commit('two', rev_id='m2')
230+
231+ os.chdir('checkout')
232+ out, err = self.run_bzr('update -r revid:m2')
233+ self.assertEqual('', out)
234+ self.assertEqual('+N file2\nAll changes applied successfully.\n'
235+ 'Updated to revision 2.\n', err)
236
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 """
242 raise NotImplementedError(self.unlock)
243
244- def update(self, change_reporter=None, possible_transports=None):
245+ _marker = object()
246+
247+ def update(self, change_reporter=None, possible_transports=None,
248+ revision=None, old_tip=_marker):
249 """Update a working tree along its branch.
250
251 This will update the branch if its bound too, which means we have
252@@ -2201,10 +2204,16 @@
253 - Merge current state -> basis tree of the master w.r.t. the old tree
254 basis.
255 - Do a 'normal' merge of the old branch basis if it is relevant.
256+
257+ :param revision: The target revision to update to. Must be in the
258+ revision history.
259+ :param old_tip: If branch.update() has already been run, the value it
260+ returned (old tip of the branch or None). _marker is used
261+ otherwise.
262 """
263 if self.branch.get_bound_location() is not None:
264 self.lock_write()
265- update_branch = True
266+ update_branch = (old_tip is self._marker)
267 else:
268 self.lock_tree_write()
269 update_branch = False
270@@ -2212,13 +2221,14 @@
271 if update_branch:
272 old_tip = self.branch.update(possible_transports)
273 else:
274- old_tip = None
275- return self._update_tree(old_tip, change_reporter)
276+ if old_tip is self._marker:
277+ old_tip = None
278+ return self._update_tree(old_tip, change_reporter, revision)
279 finally:
280 self.unlock()
281
282 @needs_tree_write_lock
283- def _update_tree(self, old_tip=None, change_reporter=None):
284+ def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
285 """Update a tree to the master branch.
286
287 :param old_tip: if supplied, the previous tip revision the branch,
288@@ -2239,12 +2249,17 @@
289 last_rev = self.get_parent_ids()[0]
290 except IndexError:
291 last_rev = _mod_revision.NULL_REVISION
292- if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
293- # merge tree state up to new branch tip.
294+ if revision is None:
295+ revision = self.branch.last_revision()
296+ else:
297+ if revision not in self.branch.revision_history():
298+ raise errors.NoSuchRevision(self.branch, revision)
299+ if last_rev != _mod_revision.ensure_null(revision):
300+ # merge tree state up to specified revision.
301 basis = self.basis_tree()
302 basis.lock_read()
303 try:
304- to_tree = self.branch.basis_tree()
305+ to_tree = self.branch.repository.revision_tree(revision)
306 if basis.inventory.root is None:
307 self.set_root_id(to_tree.get_root_id())
308 self.flush()
309@@ -2254,11 +2269,12 @@
310 basis,
311 this_tree=self,
312 change_reporter=change_reporter)
313+ self.set_last_revision(revision)
314 finally:
315 basis.unlock()
316 # TODO - dedup parents list with things merged by pull ?
317 # reuse the tree we've updated to to set the basis:
318- parent_trees = [(self.branch.last_revision(), to_tree)]
319+ parent_trees = [(revision, to_tree)]
320 merges = self.get_parent_ids()[1:]
321 # Ideally we ask the tree for the trees here, that way the working
322 # tree can decide whether to give us the entire tree or give us a
323@@ -2294,8 +2310,7 @@
324 # should be able to remove this extra flush.
325 self.flush()
326 graph = self.branch.repository.get_graph()
327- base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
328- old_tip)
329+ base_rev_id = graph.find_unique_lca(revision, old_tip)
330 base_tree = self.branch.repository.revision_tree(base_rev_id)
331 other_tree = self.branch.repository.revision_tree(old_tip)
332 result += merge.merge_inner(