# HG changeset patch
# User Georges Racinet <georges.racinet@octobus.net>
# Date 1592312367 -7200
#      Tue Jun 16 14:59:27 2020 +0200
# Node ID 6eddb15037298509b2b8e82b7f05963fa568a717
# Parent  df655a5c3c66ad4512b5bf75850e383159d4ddc3
FindCommit: 'revision' is actually a Git revspec.

This is a nice example of how a guess based on the type of an
argument can be wrong.

First tries with actual GitLab client showed that this revision
is called "id_or_ref" on the client side, and can be either
- a SHA ("id")
- a GitLab branch name ("ref")
- HEAD, meaning the head of the default branch,
  not supported yet in HGitaly.

Just hoping there's not more advanced syntax such as `master~3`
that would force us to do a full conversion from Git revspec to
Mercurial revset syntax.

diff --git a/hgitaly/servicer.py b/hgitaly/servicer.py
--- a/hgitaly/servicer.py
+++ b/hgitaly/servicer.py
@@ -4,11 +4,15 @@
     error as hg_error,
     hg,
     node,
+    scmutil,
     ui as uimod,
 )
 from mercurial.utils import stringutil as hg_stringutil
 from google.protobuf.timestamp_pb2 import Timestamp
 
+# TODO move that to heptapod.gitlab
+from hgext3rd.heptapod.git import parse_gitlab_branch
+
 from .stub.shared_pb2 import (
     CommitAuthor,
     GitCommit,
@@ -72,6 +76,15 @@
                      )
 
 
+def gitlab_branch_to_branchmap_branch(gl_branch):
+    parsed = parse_gitlab_branch(gl_branch)
+    if parsed is None:
+        return None  # could be a SHA, a bookmark or a tag
+    if parsed[1] is None:
+        return parsed[0]
+    return b':'.join(parsed)
+
+
 class Servicer(CommitServiceServicer):
     """CommitService implementation.
 
@@ -105,7 +118,14 @@
         repo = self.load_repo(request.repository)
         logger.debug("FindCommit revision=%r", request.revision)
         try:
-            ctx = repo[request.revision]
+            branchmap_key = gitlab_branch_to_branchmap_branch(request.revision)
+            if branchmap_key is None:
+                ctx = scmutil.revsingle(repo, request.revision)
+            else:
+                contexts = (repo[node]
+                            for node in repo.branchmap()[branchmap_key])
+                ctx = max((ctx.rev(), ctx) for ctx in contexts)[1]
+
         except hg_error.RepoLookupError:
             commit = None
         else:
diff --git a/hgitaly/tests/test_commit.py b/hgitaly/tests/test_commit.py
--- a/hgitaly/tests/test_commit.py
+++ b/hgitaly/tests/test_commit.py
@@ -7,7 +7,9 @@
 
 def test_find_commit(grpc_stub, server_repos_root):
     repo_path = server_repos_root / 'repo1'
-    wrapper = LocalRepoWrapper.init(repo_path)
+    wrapper = LocalRepoWrapper.init(repo_path,
+                                    config={b'extensions': {b'topic': b''}}
+                                    )
     now = time.time()
     ctx = wrapper.write_commit('foo',
                                utc_timestamp=now,
@@ -18,7 +20,7 @@
 
     request = FindCommitRequest(
         repository=Repository(relative_path='repo1'),
-        revision=ctx.node())
+        revision=ctx.hex())
     response = grpc_stub.FindCommit(request)
 
     commit = response.commit
@@ -31,7 +33,7 @@
 
     request = FindCommitRequest(
         repository=Repository(relative_path='repo1'),
-        revision=ctx2.node())
+        revision=ctx2.hex())
     response = grpc_stub.FindCommit(request)
 
     commit2 = response.commit
@@ -45,6 +47,26 @@
 
     request = FindCommitRequest(
         repository=Repository(relative_path='repo1'),
-        revision=b'.' * 20)
+        revision=b'branch/default')
+    response = grpc_stub.FindCommit(request)
+    assert response.commit == commit2
+
+    # TODO reuse heptapod testhelpers
+    wrapper.command(b'topics', b'antelope')
+    wrapper.write_commit('animals',
+                         message="in topic",
+                         parent=ctx)
+    request = FindCommitRequest(
+        repository=Repository(relative_path='repo1'),
+        revision=b'branch/default')
+    response = grpc_stub.FindCommit(request)
+
+    commit_top = response.commit
+    assert commit_top is not None
+    assert commit_top.subject == b"in topic"
+
+    request = FindCommitRequest(
+        repository=Repository(relative_path='repo1'),
+        revision=b'unknown')
     response = grpc_stub.FindCommit(request)
     assert not response.HasField('commit')