diff --git a/hgitaly/service/ref.py b/hgitaly/service/ref.py
index 7c9cb5c847d93ce5b2934679523ccbad4c4f8f8f_aGdpdGFseS9zZXJ2aWNlL3JlZi5weQ==..4917ed9df786dfbb8ed18d967a764720b38be3eb_aGdpdGFseS9zZXJ2aWNlL3JlZi5weQ== 100644
--- a/hgitaly/service/ref.py
+++ b/hgitaly/service/ref.py
@@ -6,5 +6,8 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 import logging
 
+from heptapod.gitlab.branch import (
+    gitlab_branch_from_ref,
+)
 from hgext3rd.heptapod.branch import get_default_gitlab_branch
 
@@ -9,5 +12,8 @@
 from hgext3rd.heptapod.branch import get_default_gitlab_branch
 
+from ..stub.shared_pb2 import (
+    Branch
+)
 from ..stub.ref_pb2 import (
     FindDefaultBranchNameRequest,
     FindDefaultBranchNameResponse,
@@ -17,7 +23,11 @@
     FindAllTagNamesResponse,
     FindRefNameRequest,
     FindRefNameResponse,
+    FindAllBranchesRequest,
+    FindAllBranchesResponse,
+    FindBranchRequest,
+    FindBranchResponse,
 )
 from ..stub.ref_pb2_grpc import RefServiceServicer
 
 from ..branch import (
@@ -20,6 +30,7 @@
 )
 from ..stub.ref_pb2_grpc import RefServiceServicer
 
 from ..branch import (
+    gitlab_branch_head,
     iter_gitlab_branches,
 )
@@ -24,5 +35,6 @@
     iter_gitlab_branches,
 )
+from .commit import changectx_to_git_commit as git_commit
 from ..servicer import HGitalyServicer
 from ..util import chunked
 
@@ -106,3 +118,29 @@
         in the whole Rails application.
         """
         raise NotImplementedError("Second round of implementation")
+
+    def FindBranch(self,
+                   request: FindBranchRequest,
+                   context) -> FindBranchResponse:
+        repo = self.load_repo(request.repository)
+        name = request.name
+        if name.startswith(b'refs/'):
+            name = gitlab_branch_from_ref(request.name)
+
+        if name is None:
+            # TODO SPEC check if we really must exclude other refs
+            return FindBranchResponse(branch=None)
+
+        head = gitlab_branch_head(repo, name)
+        return FindBranchResponse(
+            branch=Branch(name=name, target_commit=git_commit(head)))
+
+    def FindAllBranches(self,
+                        request: FindAllBranchesRequest,
+                        context) -> FindAllBranchesResponse:
+        Branch = FindAllBranchesResponse.Branch
+        repo = self.load_repo(request.repository)
+        for chunk in chunked(iter_gitlab_branches(repo)):
+            yield FindAllBranchesResponse(
+                branches=(Branch(name=name, target=git_commit(head))
+                          for name, head in chunk))
diff --git a/hgitaly/tests/test_ref.py b/hgitaly/tests/test_ref.py
index 7c9cb5c847d93ce5b2934679523ccbad4c4f8f8f_aGdpdGFseS90ZXN0cy90ZXN0X3JlZi5weQ==..4917ed9df786dfbb8ed18d967a764720b38be3eb_aGdpdGFseS90ZXN0cy90ZXN0X3JlZi5weQ== 100644
--- a/hgitaly/tests/test_ref.py
+++ b/hgitaly/tests/test_ref.py
@@ -9,6 +9,8 @@
 from ..stub.ref_pb2 import (
     FindAllBranchNamesRequest,
     FindAllTagNamesRequest,
+    FindBranchRequest,
+    FindAllBranchesRequest,
 )
 from ..stub.ref_pb2_grpc import RefServiceStub
 
@@ -60,3 +62,38 @@
 
     resp = ref_stub.FindAllTagNames(request)
     assert [chunk.names for chunk in resp] == [[b'v3.2.1']]
+
+
+def test_find_branch(grpc_channel, server_repos_root):
+    ref_stub = RefServiceStub(grpc_channel)
+    wrapper, grpc_repo = make_empty_repo(server_repos_root,
+                                         relative_path='find_branch')
+    ctx = wrapper.write_commit('foo')
+    resp = ref_stub.FindBranch(
+        FindBranchRequest(repository=grpc_repo,
+                          name=b'branch/default'))
+    branch = resp.branch
+    assert branch is not None
+    assert branch.name == b'branch/default'
+    assert branch.target_commit.id == ctx.hex().decode()
+
+    resp = ref_stub.FindBranch(
+        FindBranchRequest(repository=grpc_repo,
+                          name=b'refs/heads/branch/default'))
+    assert resp.branch == branch
+
+    resp = ref_stub.FindBranch(
+        FindBranchRequest(repository=grpc_repo,
+                          name=b'refs/keeparound/012ca34fe56'))
+
+    # There is no None in gRPC, just cascading default content (empty string).
+    # We checked that Gitaly indeed uses the default `Branch(name=b'')`
+    # to represent the absence of results.
+    assert not resp.branch.name
+
+    resp = ref_stub.FindAllBranches(
+        FindAllBranchesRequest(repository=grpc_repo))
+    branches = [br for chunk in resp for br in chunk.branches]
+    assert len(branches) == 1
+    assert branches[0].name == b'branch/default'
+    assert branches[0].target == branch.target_commit