"update -r REVNO" support

Robert Widhopf-Fenk hack at robf.de
Sat Aug 4 03:16:39 BST 2007


Hi,

"revert -r" was not enough for me, so I tried to implement
"update -r" and better "bzr st" for outdated working trees.

;-)

Robert.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.19)
# revision_id: hack at robf.de-20070804020549-fqaszg0ddxizsqm7
# target_branch: file:///home/fenk/download/bzr.repo/bzr.dev/
# testament_sha1: a25381b23592bbcff486c0cdb00a13422253e109
# timestamp: 2007-08-04 04:09:14 +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/builtins.py'
--- bzrlib/builtins.py	2007-07-31 20:02:47 +0000
+++ bzrlib/builtins.py	2007-08-04 02:05:49 +0000
@@ -1013,9 +1013,10 @@
 
     _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,10 +1024,18 @@
         else:
             tree.lock_tree_write()
         try:
+	    if revision:
+		if len(revision) > 1:
+		    raise errors.BzrCommandError(
+			'bzr update --revision takes exactly 1 revision value')
+		branch_location = osutils.getcwd()
+		source = Branch.open(branch_location)
+		to_rev = _mod_revision.ensure_null(revision[0].in_history(source)[1])
+	    else:
+		to_rev = _mod_revision.ensure_null(tree.branch.last_revision())
             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()):
@@ -1034,7 +1043,8 @@
                     note("Tree is up to date at revision %d." % (revno,))
                     return 0
             conflicts = tree.update(delta._ChangeReporter(
-                                        unversioned_filter=tree.is_ignored))
+                                        unversioned_filter=tree.is_ignored),
+				    revision and to_rev or None)
             revno = tree.branch.revision_id_to_revno(
                 _mod_revision.ensure_null(tree.last_revision()))
             note('Updated to revision %d.' % (revno,))

=== modified file 'bzrlib/status.py'
--- bzrlib/status.py	2007-07-23 14:27:42 +0000
+++ bzrlib/status.py	2007-08-04 00:14:16 +0000
@@ -122,7 +122,12 @@
         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_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 ""))
             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-04 01:53:59 +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 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 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-04 02:05:49 +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,12 +2037,12 @@
                 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=None):
         """Update a tree to the master branch.
 
         :param old_tip: if supplied, the previous tip revision the branch,
@@ -2063,12 +2063,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
+        if to_rev is None:
+            to_rev = _mod_revision.ensure_null(self.branch.last_revision())
+        if last_rev != 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 +2099,8 @@
                 parent_trees.append(
                     (old_tip, self.branch.repository.revision_tree(old_tip)))
             self.set_parent_trees(parent_trees)
+            if to_revision is not None:
+                self.set_parent_ids([to_rev])
             last_rev = parent_trees[0][0]
         else:
             # the working tree had the same last-revision as the master

# Begin bundle
IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWfZYZYoADAj/gFR0BgB/////
fyGfjv////BgEjx732dzmp06yLgZCgAAADUZTA0ULYapsrQiiFjSwbNMcwmgNAaNGEaDEaYmTE0G
EaBkAyYHMJoDQGjRhGgxGmJkxNBhGgZAMmA0mJoEp6hkDTQAAAAaAAABoAJEiZCniJPU9NJ5TyJ6
E1MjBNqPQIA0epkGmmgIlII1PSMACmAQwE9VP9JTeqDQ0aZMjTTR6NIFSiACAJpoCZT0JMJphT1G
zVNqAABpkyXWDSAGKgNoGlr4tHNpbGzm23bt++d9K8venSN8Wy6UerG5tcLnyTkz3OFMD3/D2v8N
A12siUWZGabs8JHbz00Hc2EGOX6XcPaGLJoqxLeMLRhLFlKziq7V7tYct3g7FcIsVzpE0Us11cmk
q1bqW5bgwS4IiKVtpGiE0xg0exwexMaigkQqCQOKTg9JqBYhaeIRtoSIzh5x31pQCx+D+H7/9KP5
XeWzz/FNw7jLZ0H+oEfkQuF/BDchEkSSSSQkkJFkA9/4gOzoutS9MuAzHTQsMM5YUnRDnJEG5qGO
yOVlLcguKlhytQMHmjJl8QxmAo4SEYUNGiwQ4RkAwiYMMoZ5sgeH2LNsC0lbr8lwg5+Vc3Wlhm4E
pDSD5gXQGEaCqYEpg0xwLpfEn1nisdHsYxmyTdpr7LIXW+3zR6xa/7Zs2l8dc7sXbPxfxVxplkvi
kmzmvUXLdN+i1ddJqs6HGaL8aoKRA2NjYO04cTXPDELlDkYXCCo0bHRevpi1pZWZxrvW32Nemtrd
cqzfn87RtY9d5183Pz8/Q8488uPkPYi5HEVOm8gzcaU+xjxIK26nc/C/g8cuEcZ4fhbz4S4qwUqS
lkWvSdqOo1TXEdndQgvL78XknclqFx60FS+DGBiKB1ZUolElhLz8/MYVak+0SgIIGSN8UNt5g/WY
Mi6pcWC6mH8gxDZTV0eM4ZAxoNZDIN8sA8FpIKE6ki1mFV9akq56GI8tmuFzbV2xLGRlYMCVwEKj
K0HALAYWb7rq2m1mi3BaycdZ/WHHxhnu+Nt7Ca+RpmUZZghuDwTzDcJ8yBf8TkgYRR6g1G03p5hW
ZDItYdyuAJtCoD4fB9DQHglQNFqsUPPv8ozhMrC3AilDSsjMBc9LbCwlvOoJpCXoOcICnRU0FzsN
cbLCrY2MGDCRjuRVJd0LNQlf5NUIMrqERCiIiIiIPai8YIr5i23YtpwIA54Jgikg7ksVlNlqV2A0
LQjRWLEamS9VajEn4FqyFJQelTyLq/MutizNFMS+vnjNYsKC0roorNVyRL4utkmMui+KWSMFCihQ
oqLTFisywXlrBivLlbkkliqlMWcavVaiSxiYqyxfH3WMc3p+lqTIy+1qbWxtO9vWqqKMnmOkfy9c
29mqYPaHXlwrCcO2qduVYpwVWW5s8dL5hFs88rBM4MCEQDL5IhL9maTjHTgtCxlhBUa1qEUJoc3n
JxZATGimWq6KYZ5arNmjRnaGY5iOAtUyUmTf2pPDAm7YuXNq45L9rBhdSlM2kkpnbDFvU8LMJgSk
bqKNjiu1z2GEXKdOWxZjVdLM96jVawWY6pPCvMxanQzbljkUaubR1xWNzoy9p8JwspO5qYUKYLVE
sMttrCFI7jOlJCGlTUBOSIZe1AVL0SkE7QuKNjfW5y2FLOBc9UZdZsUwa0UtY7FSheyar166uzb4
eG5vvjRVtWEycTg3EYOLobWbe7WC5Vc0LHFa6o3Q23a0pXDTlKt+7Bg3ZG20ojCWm1WKTKxlQsMV
q8Xjm+mFTgzXrDJk3rXPnteDiylHGG5cUt1sLG9SW2PHFyanaozM7TdYu0JyXqKqPAl61g0bFHBx
R2o2OnDuctVKU0xuaVvm3pSuVJeJPSkc1MmLJJsaq56U2MZJaTvYrc1t7BVhMHXrxZ6F/Fycn3kt
NHVx4tzC+mMaKdcl0t5LFubVtWxRTNfFwssX7NjaTqk5GLlHa7Dv3NjBuVb29m5sDBodZVauWcsm
dqld/Mss13K14F+Bcp2oy3YwY2QuKJORayXlqduTsyq7eDRXOHRVVY6Mxo2672rIuvODsb5eYN+e
dy8vKubc5MdmcOBU4t7Na3Kqsy0zb3BgxcWKi5sknWl7HlZv23K1UxtX2RwtkmuBWOGjatX72sb2
cuc1tWhc04rprfk4STPdauuZ4KW9bFNC2zFlJOTt0VXmDVisMm8tVWvuw4/FTPDd2K7tVFFcFaLK
ObhLFEkl6md9rxzfXZ3x0cGU0WVpTsZrnI5OHTJXGTsW8jJwzdVjHOOwuaO6SbZDg3sFGrJRiqub
l6qrJmePfPowdMtHCmzrZdW2itlq1qiTsXN6xblbh2Oaw4GLBmZFVHBg4tsW9hV2YxtptaGu1uVZ
bTRgqxu0UVxbV7OLb1jY1ZObYzL1r4713xJ+vW27DnzucXPRvwupSyjjyiwoLkXwpWxhHPLHHiyM
LFua6tuMvM3BwYubZMu6mVHNc2uxk87y5gy3xvUUbT27C9mXMm302LGje3yT05ku+jZ9l1JSirbF
1DeiciXg6MVdBcShQKxbQS3aJ9XXCQTIqIP0iiPGJ9P51m7ee6KFTNFFUpR4PoSooVLFoE1EYxkZ
CEapCEIV0wVmrCUsDan3C96e1KkuSglqXFEqStLDvDwUXiszcGmcCRILIwJPwv1eRk0HuMH5fkNa
as5rKpZs3uyl4jiOMZ8bNqBmTwPVRD/wV95MwOJK0XZO0JfNucj1y184zIZwHf5uYkEvScpcheVd
2JG0hEorRJQvpUCWuQJQK7ChYkCnUySKGDqDIPGOHSsd9jbGDYigWMVVbMDNeTze0gl3JFTrJhEN
ttj8aMRLyHiIErj6Q+L7HJFjy+7N+DBwNVXcdy1ceKxesf3Te2v57WxatUj9nk5SJPg6djxMHFo9
x3Fq/uxsUq2MnQvdyP2EWHmV+4q/GPl79El66OTcskKGP8lpOi2eNUh+dqnvk6j8xlj8qo5xJ3Tb
OuK58uznFyvH3vmZrnJV5LnyVPZYbi58nz+2Nj3PidP+cmS9V8FG9sbnY+qSD6HuknHio/SW4zuL
E6E6z7P2W4VbAtP3kO4MoNvF8C9HesI/rRHj6n63meL0ervYYOq9Y8Xm8ly5a8XyMF5iwaM1j1Ys
m1sXxtZHo1Vej36lrRo1au+VXOLc966nKcGjV9alIKeCPNR4qEhIMIQ01pFVzaRS3ug2hgS2u5W7
OMf+LFKpWl6j3fXW82u1V4382JxdXRaxdzqwwpTRauaO5exYmK5k8ZJvkm5ukLjy/VuXxq1WO+SS
3LvPpltqpcb36pE73oZO92vRaqxbR2M3Y7lH7+I90iSxy7nb/imjmptauknpqF4t5re60rRxAdY6
OPbgKzhWhXCFRuwJ7qqyykB9zc4W0L2oGrRIWolaygxMIEbA5OT5hbDb1wp8casl48oxdz0WqnvU
dpN77c79laNn9Nmu150aNzY2vV71jPgq7u1+IpFPtSc+aPLt20k7IoljpC4joXKlEwekwb3vPitS
PPwmGL8aNMnDcshLHe8Nrpmvih5+dW36XWCduv37OT5L2BxoZT1VoIFDjNOwpzVIc2PufQJnlJbJ
uI904UPJFFeLNHZNYVinjk4TGg8sRU5LVS4wp7C0xPeSgEJUdktHGsF3yJKRHk8vLzY/B8XxatV7
3u1iwWKtHzvgowUWs1qx5rG+P4sFixoq9TF9hexUaF61JXmBkJXHAYhpzjEsX5vzQbIPKgsPIdQ2
QUKBYpZ1lEbALqG1OtsV6uj3gT9daui3UKxcET2ZzRDHPklg+3WCQmMF/2mk60hnYQJqAZAOEyAc
DIBlCgj5GkgZSBJWb3Z1LwVrQ705hAuKtPamzvPOvQmIAPrZRtJjMkfp9/ypX+HEPewZeh+04I3q
CNaWDQTN1cATTdzpUmhpQbzCMPjx96XJemFcEVkFGQkjkanFd8RUKzaOX1CgbtrfxFafRUhjwAb+
a0Tp0p0jCH0IePqDUiuNX2aCqHnDlBLFKxTG1pAyKHi+IZTR2TcmQ428opIjdSF3B4PWEb4xmrs3
/lOZN85ud+3T64r2RRM9m22bDiP7A4ubXGRY3uuiTf9UfNG5FrB+nvq0h1R7e3BGsZRsSeaOcu2U
GuJaSGaj/VCJWZ6KtVEbalTWJkFwqrSKS1DssY0SYXEqnbFGd0n83yRxhg+bWe02RmMolVIK0iTU
5F60LPo39KUSh8bLH2ZriP3ScQOjt7z1pb2k3K/jTyseixgHhEM6KH2g4UoNZ4lvPATyXZfqJaWZ
wTU4fTYJhUt8EwIHA11XxDi3/Z7PqHg+gYSCQ3XtQFxvMiGvhzpQru+ZF2HOm9PNQ5Vcr7h8iIEM
QugF1oaSPaEmFDyQ8W7IGL1o3RuRSNfWI/LaT1G4qKHu/zjCfQ9pWtDMQaDhZ0qZusKTF3RpWhWE
M1sfsEQixhOgI62bgXtYt2KxGFnSgWCMwkDDi5V6ZICBYa0S0TPskjpYeBExED2onCSvnaiJHgnE
YQnJpKu3UrWpE/pRTPm7FFvTR07IX9AJtMktzwaSYMQ2Iz3oUkB6cTZuqnKMpMmS/LoiYekskP0Z
cBz6bBjD76ouENHfJkGLn3hFgld13OOfetKUBI+36fC5dZGgu4Wg3qI40mtsNsh+mzzvfkT1HInh
JSWpSopE7/hTcRdbQ5SztNyMR6uHeYlt1qfYuc6LsEr76RNayxYKX3fKPCKoyk1hkzGc+SFRYtbv
qtFrFG6kmwxipM+sUmufFyUViLIylXF5wpgjlai9M3wp4Ti/L70YJ5elPWx28kL3jRfv1gjBD7cC
J2JiGV7HtFZWowgRoihUkDlzPOWJ6CRNhyh9bb4J7wxPfy9Yl/IHkBDjFvsD56fQ7GjsUjx5lgZg
oK0v8XMlJ2E20jG4KpIfVRGLGH7ePjj7bFZ2DMZ+tAyncQ+1DbpsOiYqZDu7Wi54dPAMgFrYqbtH
MnSaUh3rVBa2g1iYAdlEwlY1ZBsfr/W/IRsA+i9GSEejakoRQD/xdyRThQkPZYZYoA==


More information about the bazaar mailing list