Rev 3355: Add Branch.set_last_revision_info smart request. (Andrew Bennetts) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Apr 10 11:53:58 BST 2008


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 3355
revision-id:pqm at pqm.ubuntu.com-20080410105350-e0c4d8n5hqgzqzi7
parent: pqm at pqm.ubuntu.com-20080410091416-1so11izeyk0tsbk9
parent: andrew.bennetts at canonical.com-20080408081821-vs80hwo6kt0fm8sr
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Thu 2008-04-10 11:53:50 +0100
message:
  Add Branch.set_last_revision_info smart request. (Andrew Bennetts)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/smart/branch.py         branch.py-20061124031907-mzh3pla28r83r97f-1
  bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
  bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
  bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
    ------------------------------------------------------------
    revno: 3297.3.5.1.4
    revision-id:andrew.bennetts at canonical.com-20080408081821-vs80hwo6kt0fm8sr
    parent: andrew.bennetts at canonical.com-20080408081333-lov0qqto26hyycoj
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: smart-set-revision-info
    timestamp: Tue 2008-04-08 18:18:21 +1000
    message:
      Add NEWS entry.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
    ------------------------------------------------------------
    revno: 3297.3.5.1.3
    revision-id:andrew.bennetts at canonical.com-20080408081333-lov0qqto26hyycoj
    parent: andrew.bennetts at canonical.com-20080408074439-n3j4ic7bcliq9s05
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: smart-set-revision-info
    timestamp: Tue 2008-04-08 18:13:33 +1000
    message:
      Add more tests, handle NoSuchRevision in case the remote branch's format can raise it.
    modified:
      bzrlib/smart/branch.py         branch.py-20061124031907-mzh3pla28r83r97f-1
      bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
    ------------------------------------------------------------
    revno: 3297.3.5.1.2
    revision-id:andrew.bennetts at canonical.com-20080408074439-n3j4ic7bcliq9s05
    parent: andrew.bennetts at canonical.com-20080408063834-o4mid7woclibs6yj
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: smart-set-revision-info
    timestamp: Tue 2008-04-08 17:44:39 +1000
    message:
      Add backwards compatibility for servers older than 1.4.
    modified:
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/smart/branch.py         branch.py-20061124031907-mzh3pla28r83r97f-1
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
    ------------------------------------------------------------
    revno: 3297.3.5.1.1
    revision-id:andrew.bennetts at canonical.com-20080408063834-o4mid7woclibs6yj
    parent: andrew.bennetts at canonical.com-20080407104900-qbxxs53xvmwzzo7k
    parent: andrew.bennetts at canonical.com-20080325072121-67qctpl7mxhr3onc
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: smart-set-revision-info
    timestamp: Tue 2008-04-08 16:38:34 +1000
    message:
      Merge 'Add Branch.set_last_revision_info smart method'.
    modified:
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/smart/branch.py         branch.py-20061124031907-mzh3pla28r83r97f-1
      bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
      bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
    ------------------------------------------------------------
    revno: 2892.2.1
    revision-id:andrew.bennetts at canonical.com-20080325072121-67qctpl7mxhr3onc
    parent: pqm at pqm.ubuntu.com-20071006144547-0e1mpht72yd6wyfz
    committer: Andrew Bennetts <andrew.bennetts at canonical.com>
    branch nick: smart-set-last-revision
    timestamp: Tue 2008-03-25 18:21:21 +1100
    message:
      Add Branch.set_last_revision_info smart method, and make the RemoteBranch client use it.
    modified:
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/smart/branch.py         branch.py-20061124031907-mzh3pla28r83r97f-1
      bzrlib/smart/request.py        request.py-20061108095550-gunadhxmzkdjfeek-1
      bzrlib/tests/branch_implementations/test_revision_history.py test_revision_histor-20070326062311-v7co92liyuchb80w-1
      bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
      bzrlib/tests/test_smart.py     test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
=== modified file 'NEWS'
--- a/NEWS	2008-04-10 06:03:33 +0000
+++ b/NEWS	2008-04-10 10:53:50 +0000
@@ -43,6 +43,11 @@
 
   IMPROVEMENTS:
 
+    * The smart protocol now has support for setting branches' revision info
+      directly.  This should make operations like push slightly faster, and is a
+      step towards server-side hooks.  The new request method name is
+      ``Branch.set_last_revision_info``.  (Andrew Bennetts)
+
     * ``bzr commit --fixes`` now recognises "gnome" as a tag by default.
       (James Westby, Andrew Cowie)
 

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2008-04-07 10:34:57 +0000
+++ b/bzrlib/remote.py	2008-04-08 07:44:39 +0000
@@ -42,7 +42,7 @@
     deprecated_method,
     zero_ninetyone,
     )
-from bzrlib.revision import NULL_REVISION
+from bzrlib.revision import ensure_null, NULL_REVISION
 from bzrlib.trace import mutter, note, warning
 
 # Note: RemoteBzrDirFormat is in bzrdir.py
@@ -1228,6 +1228,7 @@
         self._control_files = None
         self._lock_mode = None
         self._lock_token = None
+        self._repo_lock_token = None
         self._lock_count = 0
         self._leave_lock = False
 
@@ -1488,10 +1489,24 @@
     def is_locked(self):
         return self._lock_count >= 1
 
+    @needs_write_lock
     def set_last_revision_info(self, revno, revision_id):
-        self._ensure_real()
-        self._clear_cached_state()
-        return self._real_branch.set_last_revision_info(revno, revision_id)
+        assert type(revno) is int
+        revision_id = ensure_null(revision_id)
+        path = self.bzrdir._path_for_remote_call(self._client)
+        try:
+            response = self._client.call('Branch.set_last_revision_info',
+                path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
+        except errors.UnknownSmartMethod:
+            self._ensure_real()
+            self._clear_cached_state()
+            return self._real_branch.set_last_revision_info(revno, revision_id)
+        if response == ('ok',):
+            self._clear_cached_state()
+        elif response[0] == 'NoSuchRevision':
+            raise NoSuchRevision(self, response[1])
+        else:
+            raise errors.UnexpectedSmartServerResponse(response)
 
     def generate_revision_history(self, revision_id, last_rev=None,
                                   other_branch=None):

=== modified file 'bzrlib/smart/branch.py'
--- a/bzrlib/smart/branch.py	2007-12-05 07:09:42 +0000
+++ b/bzrlib/smart/branch.py	2008-04-08 08:13:33 +0000
@@ -123,6 +123,23 @@
         return SuccessfulSmartServerResponse(('ok',))
 
 
+class SmartServerBranchRequestSetLastRevisionInfo(
+    SmartServerLockedBranchRequest):
+    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
+    the specified branch.
+
+    New in bzrlib 1.4.
+    """
+    
+    def do_with_locked_branch(self, branch, new_revno, new_last_revision_id):
+        try:
+            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
+        except errors.NoSuchRevision:
+            return FailedSmartServerResponse(
+                ('NoSuchRevision', new_last_revision_id))
+        return SuccessfulSmartServerResponse(('ok',))
+
+
 class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
     
     def do_with_branch(self, branch, branch_token='', repo_token=''):

=== modified file 'bzrlib/smart/request.py'
--- a/bzrlib/smart/request.py	2008-03-27 06:10:18 +0000
+++ b/bzrlib/smart/request.py	2008-04-08 06:38:34 +0000
@@ -340,6 +340,9 @@
 request_handlers.register_lazy(
     'Branch.set_last_revision', 'bzrlib.smart.branch', 'SmartServerBranchRequestSetLastRevision')
 request_handlers.register_lazy(
+    'Branch.set_last_revision_info', 'bzrlib.smart.branch',
+    'SmartServerBranchRequestSetLastRevisionInfo')
+request_handlers.register_lazy(
     'Branch.unlock', 'bzrlib.smart.branch', 'SmartServerBranchRequestUnlock')
 request_handlers.register_lazy(
     'BzrDir.find_repository', 'bzrlib.smart.bzrdir', 'SmartServerRequestFindRepositoryV1')

=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py	2008-04-07 10:49:00 +0000
+++ b/bzrlib/tests/test_remote.py	2008-04-08 07:44:39 +0000
@@ -494,6 +494,114 @@
         branch.unlock()
 
 
+class TestBranchSetLastRevisionInfo(tests.TestCase):
+
+    def test_set_last_revision_info(self):
+        # set_last_revision_info(num, 'rev-id') is translated to calling
+        # Branch.set_last_revision_info(num, 'rev-id') on the wire.
+        transport = MemoryTransport()
+        transport.mkdir('branch')
+        transport = transport.clone('branch')
+        client = FakeClient([
+            # lock_write
+            (('ok', 'branch token', 'repo token'), ),
+            # set_last_revision_info
+            (('ok',), ),
+            # unlock
+            (('ok',), )], transport.base)
+
+        bzrdir = RemoteBzrDir(transport, _client=False)
+        branch = RemoteBranch(bzrdir, None, _client=client)
+        # This is a hack to work around the problem that RemoteBranch currently
+        # unnecessarily invokes _ensure_real upon a call to lock_write.
+        branch._ensure_real = lambda: None
+        # Lock the branch, reset the record of remote calls.
+        branch.lock_write()
+        client._calls = []
+        result = branch.set_last_revision_info(1234, 'a-revision-id')
+        self.assertEqual(
+            [('call', 'Branch.set_last_revision_info',
+                ('branch/', 'branch token', 'repo token',
+                 '1234', 'a-revision-id'))],
+            client._calls)
+        self.assertEqual(None, result)
+
+    def test_no_such_revision(self):
+        # A response of 'NoSuchRevision' is translated into an exception.
+        client = FakeClient([
+            # lock_write
+            (('ok', 'branch token', 'repo token'), ),
+            # set_last_revision_info
+            (('NoSuchRevision', 'revid'), ),
+            # unlock
+            (('ok',), ),
+            ])
+        transport = MemoryTransport()
+        transport.mkdir('branch')
+        transport = transport.clone('branch')
+
+        bzrdir = RemoteBzrDir(transport, _client=False)
+        branch = RemoteBranch(bzrdir, None, _client=client)
+        # This is a hack to work around the problem that RemoteBranch currently
+        # unnecessarily invokes _ensure_real upon a call to lock_write.
+        branch._ensure_real = lambda: None
+        # Lock the branch, reset the record of remote calls.
+        branch.lock_write()
+        client._calls = []
+
+        self.assertRaises(
+            errors.NoSuchRevision, branch.set_last_revision_info, 123, 'revid')
+        branch.unlock()
+
+    def lock_remote_branch(self, branch):
+        """Trick a RemoteBranch into thinking it is locked."""
+        branch._lock_mode = 'w'
+        branch._lock_count = 2
+        branch._lock_token = 'branch token'
+        branch._repo_lock_token = 'repo token'
+
+    def test_backwards_compatibility(self):
+        """If the server does not support the Branch.set_last_revision_info
+        verb (which is new in 1.4), then the client falls back to VFS methods.
+        """
+        # This test is a little messy.  Unlike most tests in this file, it
+        # doesn't purely test what a Remote* object sends over the wire, and
+        # how it reacts to responses from the wire.  It instead relies partly
+        # on asserting that the RemoteBranch will call
+        # self._real_branch.set_last_revision_info(...).
+
+        # First, set up our RemoteBranch with a FakeClient that raises
+        # UnknownSmartMethod, and a StubRealBranch that logs how it is called.
+        transport = MemoryTransport()
+        transport.mkdir('branch')
+        transport = transport.clone('branch')
+        client = FakeClient(
+            [(('unknown verb', 'Branch.set_last_revision_info',), ),],
+            transport.base)
+        bzrdir = RemoteBzrDir(transport, _client=False)
+        branch = RemoteBranch(bzrdir, None, _client=client)
+        class StubRealBranch(object):
+            def __init__(self):
+                self.calls = []
+            def set_last_revision_info(self, revno, revision_id):
+                self.calls.append(
+                    ('set_last_revision_info', revno, revision_id))
+        real_branch = StubRealBranch()
+        branch._real_branch = real_branch
+        self.lock_remote_branch(branch)
+
+        # Call set_last_revision_info, and verify it behaved as expected.
+        result = branch.set_last_revision_info(1234, 'a-revision-id')
+        self.assertEqual(
+            [('call', 'Branch.set_last_revision_info',
+                ('branch/', 'branch token', 'repo token',
+                 '1234', 'a-revision-id')),],
+            client._calls)
+        self.assertEqual(
+            [('set_last_revision_info', 1234, 'a-revision-id')],
+            real_branch.calls)
+
+
 class TestBranchControlGetBranchConf(tests.TestCaseWithMemoryTransport):
     """Test branch.control_files api munging...
 

=== modified file 'bzrlib/tests/test_smart.py'
--- a/bzrlib/tests/test_smart.py	2008-03-27 06:10:18 +0000
+++ b/bzrlib/tests/test_smart.py	2008-04-08 08:13:33 +0000
@@ -476,6 +476,70 @@
             tree.branch.unlock()
 
 
+class TestSmartServerBranchRequestSetLastRevisionInfo(tests.TestCaseWithTransport):
+
+    def lock_branch(self, branch):
+        branch_token = branch.lock_write()
+        repo_token = branch.repository.lock_write()
+        branch.repository.unlock()
+        self.addCleanup(branch.unlock)
+        return branch_token, repo_token
+
+    def make_locked_branch(self, format=None):
+        branch = self.make_branch('.', format=format)
+        branch_token, repo_token = self.lock_branch(branch)
+        return branch, branch_token, repo_token
+
+    def test_empty(self):
+        """An empty branch can have its last revision set to 'null:'."""
+        b, branch_token, repo_token = self.make_locked_branch()
+        backing = self.get_transport()
+        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
+            backing)
+        response = request.execute('', branch_token, repo_token, '0', 'null:')
+        self.assertEqual(SmartServerResponse(('ok',)), response)
+
+    def assertBranchLastRevisionInfo(self, expected_info, branch_relpath):
+        branch = bzrdir.BzrDir.open(branch_relpath).open_branch()
+        self.assertEqual(expected_info, branch.last_revision_info())
+
+    def test_branch_revision_info_is_updated(self):
+        """This method really does update the branch last revision info."""
+        tree = self.make_branch_and_memory_tree('.')
+        tree.lock_write()
+        tree.add('')
+        tree.commit('First commit', rev_id='revision-1')
+        tree.commit('Second commit', rev_id='revision-2')
+        tree.unlock()
+        branch = tree.branch
+
+        branch_token, repo_token = self.lock_branch(branch)
+        backing = self.get_transport()
+        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
+            backing)
+        self.assertBranchLastRevisionInfo((2, 'revision-2'), '.')
+        response = request.execute(
+            '', branch_token, repo_token, '1', 'revision-1')
+        self.assertEqual(SmartServerResponse(('ok',)), response)
+        self.assertBranchLastRevisionInfo((1, 'revision-1'), '.')
+
+    def test_not_present_revid(self):
+        """Some branch formats will check that the revision is present in the
+        repository.  When that check fails, a NoSuchRevision error is returned
+        to the client.
+        """
+        # Make a knit format branch, because that format checks the values
+        # given to set_last_revision_info.
+        b, branch_token, repo_token = self.make_locked_branch(format='knit')
+        backing = self.get_transport()
+        request = smart.branch.SmartServerBranchRequestSetLastRevisionInfo(
+            backing)
+        response = request.execute(
+            '', branch_token, repo_token, '1', 'not-present')
+        self.assertEqual(
+            SmartServerResponse(('NoSuchRevision', 'not-present')), response)
+
+
 class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
 
     def setUp(self):
@@ -1014,6 +1078,9 @@
             smart.request.request_handlers.get('Branch.set_last_revision'),
             smart.branch.SmartServerBranchRequestSetLastRevision)
         self.assertEqual(
+            smart.request.request_handlers.get('Branch.set_last_revision_info'),
+            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
+        self.assertEqual(
             smart.request.request_handlers.get('Branch.unlock'),
             smart.branch.SmartServerBranchRequestUnlock)
         self.assertEqual(




More information about the bazaar-commits mailing list