[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