[MERGE] Re: "update -r REVNO" support

Robert Widhopf-Fenk hack at robf.de
Fri Aug 10 19:56:55 BST 2007


So here is am update which addresses the issued which
remained unsolved before ...

- A working tree test
- Documentation for the to_revision parameter
- Getting the last revision of a branch just once for status

Robert.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: hack at robf.de-20070810185038-npyxf7u76hswoj27
# target_branch: file:///home/fenk/download/bzr.repo/bzr.dev/
# testament_sha1: 48db04c43a8bf6d803e33e2e1faf0ff2b900e73f
# timestamp: 2007-08-10 20:52:52 +0200
# base_revision_id: pqm at pqm.ubuntu.com-20070810111627-h6m8b0ist3ca15ae
# 
# Begin patch
=== modified file 'NEWS'
--- NEWS	2007-08-10 10:00:53 +0000
+++ NEWS	2007-08-10 18:50:38 +0000
@@ -112,6 +112,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) 
       
     * The HTML version of the man page has been superceded by a more
       comprehensive manual called the Bazaar User Reference. This manual

=== 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-08-09 15:19:06 +0000
+++ bzrlib/builtins.py	2007-08-10 18:50:38 +0000
@@ -1005,18 +1005,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:
@@ -1024,21 +1033,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-08-09 03:39:31 +0000
+++ bzrlib/revision.py	2007-08-10 18:50:38 +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:34:13 +0000
@@ -121,8 +121,15 @@
     try:
         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'")
+	    b_rev_id = wt.branch.last_revision()
+	    wt_rev_id = wt.last_revision()
+            if wt_rev_id != b_rev_id:
+		revid_to_revno = wt.branch.get_revision_id_to_revno_map()
+                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-10 17:53:09 +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')
+        out, err = self.run_bzr('update -r 1 checkout')
+        self.assertEqual('', out)
+        self.assertEqual(err, '-D  file2\n'
+                         'All changes applied successfully.\n'
+                         'Updated to out of date revision 1.\n')
+        self.failUnlessExists('checkout/file1')
+        self.failIfExists('checkout/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/tests/workingtree_implementations/test_workingtree.py'
--- bzrlib/tests/workingtree_implementations/test_workingtree.py	2007-07-20 20:27:07 +0000
+++ bzrlib/tests/workingtree_implementations/test_workingtree.py	2007-08-10 18:48:59 +0000
@@ -471,6 +471,30 @@
         self.assertEqual(wt.get_root_id(), checkout.get_root_id())
         self.assertNotEqual(None, wt.get_root_id())
 
+    def test_update_r(self):
+        """Ensure "update -r" works.
+        """
+        wt = self.make_branch_and_tree('tree')
+        main_branch = wt.branch
+        # create an branch with two files and two revisions 
+        self.build_tree(['tree/file1'])
+        wt.add('file1')
+        wt.commit('A', rev_id='A')
+        self.build_tree(['tree/file2'])
+        # now commit to 'tree'
+        wt.add('file2')
+        wt.commit('B', rev_id='B')
+	# create an checkout and ensure it is up to date 
+        checkout = main_branch.create_checkout('checkout')
+        self.assertEqual('B', checkout.last_revision())
+        self.failUnlessExists('checkout/file1')
+        self.failUnlessExists('checkout/file2')
+        # and update checkout to revision 'A'
+        self.assertEqual(0, checkout.update(to_revision='A'))
+        self.assertEqual('A', checkout.last_revision())
+        self.failUnlessExists('checkout/file1')
+        self.failIfExists('checkout/file2')
+	
     def test_update_returns_conflict_count(self):
         # working tree formats from the meta-dir format and newer support
         # setting the last revision on a tree independently of that on the 

=== modified file 'bzrlib/workingtree.py'
--- bzrlib/workingtree.py	2007-08-01 18:23:29 +0000
+++ bzrlib/workingtree.py	2007-08-10 18:50:38 +0000
@@ -2004,7 +2004,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
@@ -2028,6 +2028,9 @@
         - 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 to_revision: if supplied, the revision id to update the
+            tree to, otherwise update to the head revision of the branch.
         """
         if self.branch.get_master_branch() is not None:
             self.lock_write()
@@ -2040,16 +2043,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
@@ -2066,12 +2072,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()
@@ -2099,6 +2108,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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


More information about the bazaar mailing list