Rev 4923: (mbp) add 'update -r' feature in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Wed Dec 23 07:15:16 GMT 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 4923 [merge]
revision-id: pqm at pqm.ubuntu.com-20091223071515-trtmbl2kklwvbqrf
parent: pqm at pqm.ubuntu.com-20091223050412-z05afly8exkncg8b
parent: mbp at sourcefrog.net-20091223063119-y50yl5nbo21dvr54
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2009-12-23 07:15:15 +0000
message:
(mbp) add 'update -r' feature
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/blackbox/test_update.py test_update.py-20060212125639-c4dad1a5c56d5919
bzrlib/tests/per_workingtree/test_workingtree.py test_workingtree.py-20060203003124-817757d3e31444fb
bzrlib/workingtree.py workingtree.py-20050511021032-29b6ec0a681e02e3
=== modified file 'NEWS'
--- a/NEWS 2009-12-23 05:04:12 +0000
+++ b/NEWS 2009-12-23 05:52:00 +0000
@@ -23,11 +23,10 @@
``locations.conf`` or ``branch.conf``.
(Ted Gould, Matthew Fuller, Vincent Ladeuil)
-* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies
- running all tests in the current module, once against a pure python
- implementation, and once against an extension (pyrex/C) implementation.
- It can be used to dramatically simplify the implementation of
- ``load_tests``. (John Arbash Meinel)
+* ``bzr update`` now takes a ``--revision`` argument. This lets you
+ change the revision of the working tree to any revision in the
+ ancestry of the current or master branch. (Matthieu Moy, Mark Hammond,
+ Martin Pool, #45719)
Bug Fixes
*********
@@ -60,6 +59,9 @@
CamelCase. For the features that were more likely to be used, we added a
deprecation thunk, but not all. (John Arbash Meinel)
+* ``WorkingTree.update`` implementations must now accept a ``revision``
+ parameter.
+
Internals
*********
@@ -71,6 +73,12 @@
Testing
*******
+* ``bzrlib.tests.permute_for_extension`` is a helper that simplifies
+ running all tests in the current module, once against a pure python
+ implementation, and once against an extension (pyrex/C) implementation.
+ It can be used to dramatically simplify the implementation of
+ ``load_tests``. (John Arbash Meinel)
+
* ``bzrlib.tests.TestCase`` now subclasses ``testtools.testcase.TestCase``.
This permits features in testtools such as getUniqueInteger and
getUniqueString to be used. Because of this, testtools version 0.9.2 or
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2009-12-21 17:24:22 +0000
+++ b/bzrlib/builtins.py 2009-12-23 05:42:33 +0000
@@ -1388,16 +1388,24 @@
If you want to discard your local changes, you can just do a
'bzr revert' instead of 'bzr commit' after the update.
+
+ If the tree's branch is bound to a master branch, it will also update
+ the branch from the master.
"""
_see_also = ['pull', 'working-trees', 'status-flags']
takes_args = ['dir?']
+ takes_options = ['revision']
aliases = ['up']
- def run(self, dir='.'):
+ def run(self, dir='.', revision=None):
+ if revision is not None and len(revision) != 1:
+ raise errors.BzrCommandError(
+ "bzr update --revision takes exactly one revision")
tree = WorkingTree.open_containing(dir)[0]
+ branch = tree.branch
possible_transports = []
- master = tree.branch.get_master_branch(
+ master = branch.get_master_branch(
possible_transports=possible_transports)
if master is not None:
tree.lock_write()
@@ -1410,20 +1418,38 @@
self.outf.encoding)
try:
existing_pending_merges = tree.get_parent_ids()[1:]
- last_rev = _mod_revision.ensure_null(tree.last_revision())
- if last_rev == _mod_revision.ensure_null(
- tree.branch.last_revision()):
- # may be up to date, check master too.
- if master is None or last_rev == _mod_revision.ensure_null(
- master.last_revision()):
- revno = tree.branch.revision_id_to_revno(last_rev)
- note('Tree is up to date at revision %d of branch %s'
- % (revno, branch_location))
- return 0
+ if master is None:
+ old_tip = None
+ else:
+ # may need to fetch data into a heavyweight checkout
+ # XXX: this may take some time, maybe we should display a
+ # message
+ old_tip = branch.update(possible_transports)
+ if revision is not None:
+ revision_id = revision[0].as_revision_id(branch)
+ else:
+ revision_id = branch.last_revision()
+ if revision_id == _mod_revision.ensure_null(tree.last_revision()):
+ revno = branch.revision_id_to_revno(revision_id)
+ note("Tree is up to date at revision %d of branch %s" %
+ (revno, branch_location))
+ return 0
view_info = _get_view_info_for_change_reporter(tree)
- conflicts = tree.update(
- delta._ChangeReporter(unversioned_filter=tree.is_ignored,
- view_info=view_info), possible_transports=possible_transports)
+ change_reporter = delta._ChangeReporter(
+ unversioned_filter=tree.is_ignored,
+ view_info=view_info)
+ try:
+ conflicts = tree.update(
+ change_reporter,
+ possible_transports=possible_transports,
+ revision=revision_id,
+ old_tip=old_tip)
+ except errors.NoSuchRevision, e:
+ raise errors.BzrCommandError(
+ "branch has no revision %s\n"
+ "bzr update --revision only works"
+ " for a revision in the branch history"
+ % (e.revision))
revno = tree.branch.revision_id_to_revno(
_mod_revision.ensure_null(tree.last_revision()))
note('Updated to revision %d of branch %s' %
=== modified file 'bzrlib/tests/blackbox/test_update.py'
--- a/bzrlib/tests/blackbox/test_update.py 2009-12-14 15:51:36 +0000
+++ b/bzrlib/tests/blackbox/test_update.py 2009-12-23 06:31:19 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006 Canonical Ltd
+# Copyright (C) 2006, 2009 Canonical Ltd
# -*- coding: utf-8 -*-
#
# This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
urlutils,
workingtree,
)
+from bzrlib.tests.script import ScriptRunner
class TestUpdate(tests.TestCaseWithTransport):
@@ -67,11 +68,11 @@
def test_update_up_to_date_checkout(self):
self.make_branch_and_tree('branch')
self.run_bzr('checkout branch checkout')
- out, err = self.run_bzr('update checkout')
- self.assertEqual('Tree is up to date at revision 0 of branch %s\n'
- % osutils.pathjoin(self.test_dir, 'branch'),
- err)
- self.assertEqual('', out)
+ sr = ScriptRunner()
+ sr.run_script(self, '''
+$ bzr update checkout
+2>Tree is up to date at revision 0 of branch .../branch
+''')
def test_update_out_of_date_standalone_tree(self):
# FIXME the default format has to change for this to pass
@@ -239,3 +240,78 @@
lightweight=True)
tree.commit('empty commit')
self.run_bzr('update checkout')
+
+ def test_update_dash_r(self):
+ # Test that 'bzr update' works correctly when you have
+ # an update in the master tree, and a lightweight checkout
+ # which has merged another branch
+ master = self.make_branch_and_tree('master')
+ os.chdir('master')
+ self.build_tree(['./file1'])
+ master.add(['file1'])
+ master.commit('one', rev_id='m1')
+ self.build_tree(['./file2'])
+ master.add(['file2'])
+ master.commit('two', rev_id='m2')
+
+ sr = ScriptRunner()
+ sr.run_script(self, '''
+$ bzr update -r 1
+2>-D file2
+2>All changes applied successfully.
+2>Updated to revision 1 of .../master
+''')
+ self.failUnlessExists('./file1')
+ self.failIfExists('./file2')
+ self.assertEquals(['m1'], master.get_parent_ids())
+
+ def test_update_dash_r_outside_history(self):
+ # Test that 'bzr update' works correctly when you have
+ # an update in the master tree, and a lightweight checkout
+ # which has merged another branch
+ master = self.make_branch_and_tree('master')
+ self.build_tree(['master/file1'])
+ master.add(['file1'])
+ master.commit('one', rev_id='m1')
+
+ # Create a second branch, with an extra commit
+ other = master.bzrdir.sprout('other').open_workingtree()
+ self.build_tree(['other/file2'])
+ other.add(['file2'])
+ other.commit('other2', rev_id='o2')
+
+ os.chdir('master')
+ self.run_bzr('merge ../other')
+ master.commit('merge', rev_id='merge')
+
+ out, err = self.run_bzr('update -r revid:o2',
+ retcode=3)
+ self.assertEqual('', out)
+ self.assertEqual('bzr: ERROR: branch has no revision o2\n'
+ 'bzr update --revision only works'
+ ' for a revision in the branch history\n',
+ err)
+
+ def test_update_dash_r_in_master(self):
+ # Test that 'bzr update' works correctly when you have
+ # an update in the master tree,
+ master = self.make_branch_and_tree('master')
+ self.build_tree(['master/file1'])
+ master.add(['file1'])
+ master.commit('one', rev_id='m1')
+
+ self.run_bzr('checkout master checkout')
+
+ # add a revision in the master.
+ self.build_tree(['master/file2'])
+ master.add(['file2'])
+ master.commit('two', rev_id='m2')
+
+ os.chdir('checkout')
+ sr = ScriptRunner()
+ sr.run_script(self, '''
+$ bzr update -r revid:m2
+2>+N file2
+2>All changes applied successfully.
+2>Updated to revision 2 of branch .../master
+''')
=== modified file 'bzrlib/tests/per_workingtree/test_workingtree.py'
--- a/bzrlib/tests/per_workingtree/test_workingtree.py 2009-11-08 05:28:57 +0000
+++ b/bzrlib/tests/per_workingtree/test_workingtree.py 2009-12-22 07:03:38 +0000
@@ -590,6 +590,20 @@
self.assertEqual(master_tree.branch.revision_history(),
tree.branch.revision_history())
+ def test_update_takes_revision_parameter(self):
+ wt = self.make_branch_and_tree('wt')
+ self.build_tree_contents([('wt/a', 'old content')])
+ wt.add(['a'])
+ rev1 = wt.commit('first master commit')
+ self.build_tree_contents([('wt/a', 'new content')])
+ rev2 = wt.commit('second master commit')
+ # https://bugs.edge.launchpad.net/bzr/+bug/45719/comments/20
+ # when adding 'update -r' we should make sure all wt formats support
+ # it
+ conflicts = wt.update(revision=rev1)
+ self.assertFileEqual('old content', 'wt/a')
+ self.assertEqual([rev1], wt.get_parent_ids())
+
def test_merge_modified_detects_corruption(self):
# FIXME: This doesn't really test that it works; also this is not
# implementation-independent. mbp 20070226
=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py 2009-12-02 18:15:55 +0000
+++ b/bzrlib/workingtree.py 2009-12-22 05:24:50 +0000
@@ -2191,7 +2191,10 @@
"""
raise NotImplementedError(self.unlock)
- def update(self, change_reporter=None, possible_transports=None):
+ _marker = object()
+
+ def update(self, change_reporter=None, possible_transports=None,
+ revision=None, old_tip=_marker):
"""Update a working tree along its branch.
This will update the branch if its bound too, which means we have
@@ -2215,10 +2218,16 @@
- Merge current state -> basis tree of the master w.r.t. the old tree
basis.
- Do a 'normal' merge of the old branch basis if it is relevant.
+
+ :param revision: The target revision to update to. Must be in the
+ revision history.
+ :param old_tip: If branch.update() has already been run, the value it
+ returned (old tip of the branch or None). _marker is used
+ otherwise.
"""
if self.branch.get_bound_location() is not None:
self.lock_write()
- update_branch = True
+ update_branch = (old_tip is self._marker)
else:
self.lock_tree_write()
update_branch = False
@@ -2226,13 +2235,14 @@
if update_branch:
old_tip = self.branch.update(possible_transports)
else:
- old_tip = None
- return self._update_tree(old_tip, change_reporter)
+ if old_tip is self._marker:
+ old_tip = None
+ return self._update_tree(old_tip, change_reporter, revision)
finally:
self.unlock()
@needs_tree_write_lock
- def _update_tree(self, old_tip=None, change_reporter=None):
+ def _update_tree(self, old_tip=None, change_reporter=None, revision=None):
"""Update a tree to the master branch.
:param old_tip: if supplied, the previous tip revision the branch,
@@ -2253,12 +2263,17 @@
last_rev = self.get_parent_ids()[0]
except IndexError:
last_rev = _mod_revision.NULL_REVISION
- if last_rev != _mod_revision.ensure_null(self.branch.last_revision()):
- # merge tree state up to new branch tip.
+ if revision is None:
+ revision = self.branch.last_revision()
+ else:
+ if revision not in self.branch.revision_history():
+ raise errors.NoSuchRevision(self.branch, revision)
+ if last_rev != _mod_revision.ensure_null(revision):
+ # merge tree state up to specified revision.
basis = self.basis_tree()
basis.lock_read()
try:
- to_tree = self.branch.basis_tree()
+ to_tree = self.branch.repository.revision_tree(revision)
if basis.inventory.root is None:
self.set_root_id(to_tree.get_root_id())
self.flush()
@@ -2268,11 +2283,12 @@
basis,
this_tree=self,
change_reporter=change_reporter)
+ self.set_last_revision(revision)
finally:
basis.unlock()
# TODO - dedup parents list with things merged by pull ?
# reuse the tree we've updated to to set the basis:
- parent_trees = [(self.branch.last_revision(), to_tree)]
+ parent_trees = [(revision, to_tree)]
merges = self.get_parent_ids()[1:]
# Ideally we ask the tree for the trees here, that way the working
# tree can decide whether to give us the entire tree or give us a
@@ -2308,8 +2324,7 @@
# should be able to remove this extra flush.
self.flush()
graph = self.branch.repository.get_graph()
- base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
- old_tip)
+ base_rev_id = graph.find_unique_lca(revision, old_tip)
base_tree = self.branch.repository.revision_tree(base_rev_id)
other_tree = self.branch.repository.revision_tree(old_tip)
result += merge.merge_inner(
More information about the bazaar-commits
mailing list