[MERGE] Re: "update -r REVNO" support
Robert Widhopf-Fenk
hack at robf.de
Wed Aug 8 03:13:05 BST 2007
On Wednesday, August 8, 2007 at 11:14:45, Martin Pool wrote:
> On 8/8/07, Robert Widhopf-Fenk <hack at robf.de> wrote:
> > On Monday, August 6, 2007 at 00:34:07, Matthew D. Fuller wrote:
> > > On Mon, Aug 06, 2007 at 03:25:45PM +1000 I heard the voice of
> > > Martin Pool, and lo! it spake thus:
> > > > > + b_revno = wt.branch.last_revision_info()[0]
> > > > > + wt_revno =
> > > > > wt.branch.revision_id_to_revno(wt.last_revision())
> > > > > + missing = b_revno - wt_revno
> > > > > + warning("Working tree is at revno %d
> > > > > missing %d revision%s!" %
> > > > > + (wt_revno, missing,
> > > > > + (missing > 1) and "s" or ""))
> > > >
> > > > Giving more information here is good.
> > >
> > > But correct information is better. The above fails twice if the
> > > working tree is on a non-mainline revision; first because
> > > revision_id_to_revno() doesn't give you an answer in that case,
> > > and second because the subtraction wouldn't give you a useful
> > > answer if it did ("How the heck am I 7.8.37.931.5 revisions out
> > > of date?!")
> > >
> > > It would probably be better just to say "Branch is at revision
> > > X, working tree at revision Y.Z".
>
> Robert, could you please for now leave the revnos out of this
> message, and just submit it with the update fixes? I don't mind
> changing the message to be more clear.
Well, I just read this message to late, but I also figured
out how to get dottet revnos.
Robert
-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.19)
# revision_id: hack at robf.de-20070808020914-2zk0idez1jmux0k6
# target_branch: file:///home/fenk/download/bzr.repo/bzr.dev/
# testament_sha1: caf213e1e0a57b7a205c0dfdd249184dc83ea96d
# timestamp: 2007-08-08 04:10:28 +0200
# base_revision_id: pqm at pqm.ubuntu.com-20070803043116-l7u1uypblmx1uxnr
#
# Begin patch
=== modified file 'NEWS'
--- NEWS 2007-08-02 07:22:05 +0000
+++ NEWS 2007-08-04 00:41:50 +0000
@@ -109,7 +109,13 @@
* Log errors from the smart server in the trace file, to make debugging
test failures (and live failures!) easier. (Andrew Bennetts)
-
+
+ * For an out dated working tree, display its revno and number of missing
+ revisions. (Robert Widhopf-Fenk)
+
+ * ``bzr update`` has -r now, i.e. you may update your working tree to
+ other/older revisions than head while retaining your changes. (Robert
+ Widhopf-Fenk)
LIBRARY API BREAKS:
=== modified file 'bzrlib/branch.py'
--- bzrlib/branch.py 2007-07-27 13:02:00 +0000
+++ bzrlib/branch.py 2007-08-08 02:01:49 +0000
@@ -222,6 +222,15 @@
# modify the return value.
return mapping
+ def revision_id_to_dotted_revno(self, revision_id):
+ """Given a revision id, return its dotted revno"""
+ try:
+ return self.revision_id_to_revno(
+ _mod_revision.ensure_null(revision_id))
+ except errors.NoSuchRevision:
+ dotted_map = self.get_revision_id_to_revno_map()
+ return '.'.join(str(i) for i in dotted_map[revision_id])
+
def _gen_revno_map(self):
"""Create a new mapping from revision ids to dotted revnos.
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2007-07-31 20:02:47 +0000
+++ bzrlib/builtins.py 2007-08-08 02:09:14 +0000
@@ -1004,18 +1004,27 @@
"""Update a tree to have the latest code committed to its branch.
This will perform a merge into the working tree, and may generate
- conflicts. If you have any local changes, you will still
- need to commit them after the update for the update to be complete.
+ conflicts. If you have any local changes, you will still need to
+ commit them after the update for the update to be complete.
If you want to discard your local changes, you can just do a
'bzr revert' instead of 'bzr commit' after the update.
+
+ If you give a --revision parameter, your working tree will be updated
+ to the given revision. Your working tree will be out of date if the
+ given revision is not the latest revision of the branch and thus you
+ cannot commit to the branch anymore.
+
+ Use 'bzr status' to check if the branch is outdated and 'bzr update'
+ to be able to commit again.
"""
_see_also = ['pull', 'working-trees', 'status-flags']
takes_args = ['dir?']
+ takes_options = ['revision']
aliases = ['up']
-
- def run(self, dir='.'):
+
+ def run(self, revision=None, dir='.'):
tree = WorkingTree.open_containing(dir)[0]
master = tree.branch.get_master_branch()
if master is not None:
@@ -1023,21 +1032,39 @@
else:
tree.lock_tree_write()
try:
+ branch_rev = _mod_revision.ensure_null(tree.branch.last_revision())
+ if revision:
+ if len(revision) > 1:
+ raise errors.BzrCommandError(
+ 'bzr update --revision takes exactly 1 revision value')
+ to_rev = _mod_revision.ensure_null(
+ revision[0].in_history(tree.branch)[1])
+ revision = to_rev
+ else:
+ to_rev = branch_rev
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()):
+ last_rev = _mod_revision.ensure_null(tree.last_revision())
+ if last_rev == to_rev:
# 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." % (revno,))
+ note("Tree is up to date at revision %s." %
+ (tree.branch.revision_id_to_revno(last_rev),))
return 0
+ # we use "revision" here instead of to_rev as in lightweighted
+ # checkouts the revision may change due to an update of the
+ # related branch.
conflicts = tree.update(delta._ChangeReporter(
- unversioned_filter=tree.is_ignored))
- revno = tree.branch.revision_id_to_revno(
- _mod_revision.ensure_null(tree.last_revision()))
- note('Updated to revision %d.' % (revno,))
+ unversioned_filter=tree.is_ignored),
+ revision)
+ # get the last revision again as it might have changed
+ last_rev = tree.last_revision()
+ if branch_rev == to_rev:
+ note('Updated to revision %s.' %
+ (tree.branch.revision_id_to_dotted_revno(last_rev),))
+ else:
+ warning('Updated to out of date revision %s.' %
+ (tree.branch.revision_id_to_dotted_revno(last_rev),))
if tree.get_parent_ids()[1:] != existing_pending_merges:
note('Your local commits will now show as pending merges with '
"'bzr status', and can be committed with 'bzr commit'.")
=== modified file 'bzrlib/revision.py'
--- bzrlib/revision.py 2007-07-11 19:04:11 +0000
+++ bzrlib/revision.py 2007-08-08 02:03:10 +0000
@@ -490,7 +490,7 @@
def ensure_null(revision_id):
- """Ensure only NULL_REVISION is used to represent the null revisionn"""
+ """Ensure only NULL_REVISION is used to represent the null revision"""
if revision_id is None:
return NULL_REVISION
else:
=== modified file 'bzrlib/status.py'
--- bzrlib/status.py 2007-07-23 14:27:42 +0000
+++ bzrlib/status.py 2007-08-08 02:04:34 +0000
@@ -122,7 +122,14 @@
new_is_working_tree = True
if revision is None:
if wt.last_revision() != wt.branch.last_revision():
- warning("working tree is out of date, run 'bzr update'")
+ revid_to_revno = wt.branch.get_revision_id_to_revno_map()
+ wt_rev_id = wt.last_revision()
+ b_rev_id = wt.branch.last_revision()
+ warning("Working tree is out of date.")
+ warning("Working revision: %s" %
+ (wt.branch.revision_id_to_dotted_revno(wt_rev_id),))
+ warning("Branch revision: %s" %
+ (wt.branch.revision_id_to_dotted_revno(b_rev_id),))
new = wt
old = new.basis_tree()
elif len(revision) > 0:
=== modified file 'bzrlib/tests/blackbox/test_update.py'
--- bzrlib/tests/blackbox/test_update.py 2007-07-04 08:46:22 +0000
+++ bzrlib/tests/blackbox/test_update.py 2007-08-08 02:09:14 +0000
@@ -196,7 +196,7 @@
# merges, because they were real merges
out, err = self.run_bzr('update')
self.assertEqual('', out)
- self.assertEndsWith(err, 'All changes applied successfully.\n'
+ self.assertContainsRe(err, 'All changes applied successfully.\n'
'Updated to revision 2.\n')
self.assertContainsRe(err, r'\+N file3')
# The pending merges should still be there
@@ -210,3 +210,49 @@
lightweight=True)
tree.commit('empty commit')
self.run_bzr('update checkout')
+
+ def test_update_r1(self):
+ """Update a checkout from revision 2 to revision 1"""
+ self.make_branch_and_tree('branch')
+ # make a checkout
+ self.run_bzr('checkout --lightweight branch checkout')
+ self.build_tree(['checkout/file1'])
+ self.run_bzr('add checkout/file1')
+ self.run_bzr('commit -m add-file1 checkout')
+ self.build_tree(['checkout/file2'])
+ self.run_bzr('add checkout/file2')
+ self.run_bzr('commit -m add-file2 checkout')
+ os.chdir('checkout')
+ out, err = self.run_bzr('update -r 1')
+ self.assertEqual('', out)
+ self.assertEqual(err, '-D file2\n'
+ 'All changes applied successfully.\n'
+ 'Updated to out of date revision 1.\n')
+ self.failUnlessExists('file1')
+ self.failIfExists('file2')
+
+ def test_update_r1_with_modifications(self):
+ """Update a checkout from revision 2 to revision 1 with changes"""
+ self.make_branch_and_tree('branch')
+ # make a checkout
+ self.run_bzr('checkout --lightweight branch checkout')
+ self.build_tree(['checkout/file1'])
+ self.run_bzr('add checkout/file1')
+ self.run_bzr('commit -m add-file1 checkout')
+ self.build_tree(['checkout/file2'])
+ self.run_bzr('add checkout/file2')
+ self.run_bzr('commit -m add-file2 checkout')
+ os.chdir('checkout')
+ a_file = file('file1', 'wt')
+ a_file.write('FooChanges\n')
+ a_file.close()
+ out, err = self.run_bzr('update -r 1')
+ self.assertEqual('', out)
+ self.assertEqual(err, '-D file2\n'
+ 'All changes applied successfully.\n'
+ 'Updated to out of date revision 1.\n')
+ self.failUnlessExists('file1')
+ self.failIfExists('file2')
+ out, err = self.run_bzr('diff', retcode=1)
+ self.assertContainsRe(out, '\n\\+FooChanges\n')
+ self.assertEqual(err, '')
=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py 2007-07-25 21:25:00 +0000
+++ bzrlib/workingtree.py 2007-08-08 02:09:14 +0000
@@ -2001,7 +2001,7 @@
"""
raise NotImplementedError(self.unlock)
- def update(self, change_reporter=None):
+ def update(self, change_reporter=None, to_revision=None):
"""Update a working tree along its branch.
This will update the branch if its bound too, which means we have
@@ -2037,16 +2037,19 @@
old_tip = self.branch.update()
else:
old_tip = None
- return self._update_tree(old_tip, change_reporter)
+ return self._update_tree(old_tip, change_reporter, to_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,
+ to_revision_id=None):
"""Update a tree to the master branch.
:param old_tip: if supplied, the previous tip revision the branch,
before it was changed to the master branch's tip.
+ :param to_revision_id: if supplied, the revision id to update the
+ tree to, otherwise update to the head revision of the branch.
"""
# here if old_tip is not None, it is the old tip of the branch before
# it was updated from the master branch. This should become a pending
@@ -2063,12 +2066,15 @@
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()):
+ to_rev = to_revision_id
+ if to_rev is None:
+ to_rev = self.branch.last_revision()
+ if last_rev != _mod_revision.ensure_null(to_rev):
# merge tree state up to new branch tip.
basis = self.basis_tree()
basis.lock_read()
try:
- to_tree = self.branch.basis_tree()
+ to_tree = self.branch.repository.revision_tree(to_rev)
if basis.inventory.root is None:
self.set_root_id(to_tree.inventory.root.file_id)
self.flush()
@@ -2096,6 +2102,7 @@
parent_trees.append(
(old_tip, self.branch.repository.revision_tree(old_tip)))
self.set_parent_trees(parent_trees)
+ self.set_last_revision(to_rev)
last_rev = parent_trees[0][0]
else:
# the working tree had the same last-revision as the master
# Begin bundle
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
xRxQ/+LuSKcKEgZkZlRg
More information about the bazaar
mailing list