Highlight the HEAD ref in the list of branches

Move it to the top and make it bold, if it exists. Only applies to the
leaf branch of the HEAD symbolic ref, not to non-branches or
non-symbolic referents of HEAD.

Change-Id: Idcac9fca76c079a7c0f5fe5149175d1065c48fee
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
index 82c5b46..6113c13 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Ordering;
 import com.google.common.util.concurrent.UncheckedExecutionException;
 
@@ -34,6 +35,7 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.annotation.Nullable;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -49,27 +51,61 @@
   }
 
   @Override
-  protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
+  protected void doGet(HttpServletRequest req, HttpServletResponse res)
+      throws IOException {
     RevWalk walk = new RevWalk(ServletUtils.getRepository(req));
-    List<Map<String, String>> tags;
+    List<Map<String, Object>> tags;
     try {
       tags = getTags(req, timeCache, walk, 0);
     } finally {
       walk.release();
     }
-    render(req, res, "gitiles.refsDetail", ImmutableMap.of(
-        "branches", getBranches(req, 0),
-        "tags", tags));
+    render(req, res, "gitiles.refsDetail",
+        ImmutableMap.of("branches", getBranches(req, 0), "tags", tags));
   }
 
-  static List<Map<String, String>> getBranches(HttpServletRequest req, int limit)
+  static List<Map<String, Object>> getBranches(HttpServletRequest req, int limit)
       throws IOException {
-    return getRefs(req, Constants.R_HEADS, Ordering.from(RefComparator.INSTANCE), limit);
+    RefDatabase refdb = ServletUtils.getRepository(req).getRefDatabase();
+    Ref head = refdb.getRef(Constants.HEAD);
+    Ref headLeaf = head != null && head.isSymbolic() ? head.getLeaf() : null;
+    return getRefs(
+        refdb,
+        ViewFilter.getView(req),
+        Constants.R_HEADS,
+        branchComparator(headLeaf),
+        headLeaf,
+        limit);
   }
 
-  static List<Map<String, String>> getTags(HttpServletRequest req, TimeCache timeCache,
-     RevWalk walk, int limit) throws IOException {
-    return getRefs(req, Constants.R_TAGS, tagComparator(timeCache, walk), limit);
+  private static Ordering<Ref> branchComparator(Ref headLeaf) {
+    if (headLeaf == null) {
+      return Ordering.from(RefComparator.INSTANCE);
+    }
+    final String headLeafName = headLeaf.getName();
+    return new Ordering<Ref>() {
+      @Override
+      public int compare(@Nullable Ref left, @Nullable Ref right) {
+        int l = isHead(left) ? 1 : 0;
+        int r = isHead(right) ? 1 : 0;
+        return l - r;
+      }
+
+      private final boolean isHead(Ref ref) {
+        return ref != null && ref.getName().equals(headLeafName);
+      }
+    }.compound(RefComparator.INSTANCE);
+  }
+
+  static List<Map<String, Object>> getTags(HttpServletRequest req,
+      TimeCache timeCache, RevWalk walk, int limit) throws IOException {
+    return getRefs(
+        ServletUtils.getRepository(req).getRefDatabase(),
+        ViewFilter.getView(req),
+        Constants.R_TAGS,
+        tagComparator(timeCache, walk),
+        null,
+        limit);
   }
 
   private static Ordering<Ref> tagComparator(final TimeCache timeCache, final RevWalk walk) {
@@ -85,20 +121,28 @@
     }).reverse().compound(RefComparator.INSTANCE);
   }
 
-  private static List<Map<String, String>> getRefs(HttpServletRequest req, String prefix,
-      Ordering<Ref> ordering, int limit) throws IOException {
-    RefDatabase refdb = ServletUtils.getRepository(req).getRefDatabase();
+  private static List<Map<String, Object>> getRefs(
+      RefDatabase refdb,
+      GitilesView view,
+      String prefix,
+      Ordering<Ref> ordering,
+      @Nullable Ref headLeaf,
+      int limit) throws IOException {
     Collection<Ref> refs = refdb.getRefs(prefix).values();
     refs = ordering.leastOf(refs, limit > 0 ? limit + 1 : refs.size());
-    List<Map<String, String>> result = Lists.newArrayListWithCapacity(refs.size());
+    List<Map<String, Object>> result = Lists.newArrayListWithCapacity(refs.size());
 
     for (Ref ref : refs) {
       String name = ref.getName().substring(prefix.length());
       boolean needPrefix = !ref.getName().equals(refdb.getRef(name).getName());
-      result.add(ImmutableMap.of(
-          "url", GitilesView.revision().copyFrom(req).setRevision(
-              Revision.unpeeled(needPrefix ? ref.getName() : name, ref.getObjectId())).toUrl(),
-          "name", name));
+      Map<String, Object> value = Maps.newHashMapWithExpectedSize(3);
+      value.put("url", GitilesView.revision().copyFrom(view).setRevision(
+        Revision.unpeeled(needPrefix ? ref.getName() : name, ref.getObjectId())).toUrl());
+      value.put("name", name);
+      if (headLeaf != null) {
+        value.put("isHead", headLeaf.equals(ref));
+      }
+      result.add(value);
     }
     return result;
   }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
index 79f90e9..eb61a25 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
@@ -64,7 +64,7 @@
     Repository repo = ServletUtils.getRepository(req);
     RepositoryDescription desc = accessFactory.forRequest(req).getRepositoryDescription();
     RevWalk walk = new RevWalk(repo);
-    List<Map<String, String>> tags;
+    List<Map<String, Object>> tags;
     Map<String, Object> data;
     try {
       tags = RefServlet.getTags(req, timeCache, walk, REF_LIMIT);
@@ -88,7 +88,7 @@
     if (!data.containsKey("entries")) {
       data.put("entries", ImmutableList.of());
     }
-    List<Map<String, String>> branches = RefServlet.getBranches(req, REF_LIMIT);
+    List<Map<String, Object>> branches = RefServlet.getBranches(req, REF_LIMIT);
 
     data.put("cloneUrl", desc.cloneUrl);
     data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl));
diff --git a/gitiles-servlet/src/main/resources/com/google/gitiles/static/gitiles.css b/gitiles-servlet/src/main/resources/com/google/gitiles/static/gitiles.css
index a41b88c..a1f8df1 100644
--- a/gitiles-servlet/src/main/resources/com/google/gitiles/static/gitiles.css
+++ b/gitiles-servlet/src/main/resources/com/google/gitiles/static/gitiles.css
@@ -156,6 +156,9 @@
   float: left;
   width: 200px;
 }
+.ref-list li.head-ref {
+  font-weight: bold;
+}
 
 
 /* Styles for the object detail templates. */
diff --git a/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RefList.soy b/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RefList.soy
index 751ec86..d13dd6d 100644
--- a/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RefList.soy
+++ b/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RefList.soy
@@ -20,7 +20,7 @@
  * @param repositoryName name of this repository.
  * @param? menuEntries menu entries.
  * @param breadcrumbs breadcrumbs for this page.
- * @param branches list of branch objects with url and name keys.
+ * @param branches list of branch objects with url, name, and isHead keys.
  * @param tags list of tag objects with url and name keys.
  */
 {template .refsDetail}
@@ -56,13 +56,13 @@
  * List of a single type of refs
  *
  * @param type name of this type of refs, e.g. "Branches"
- * @param refs list of ref objects with url and name keys
+ * @param refs list of branch objects with url, name, and optional isHead keys.
  */
 {template .refList}
   <h3>{$type}</h3>
-  <ul>
+  <ul class="ref-list">
   {foreach $ref in $refs}
-    <li><a href="{$ref.url}">{$ref.name}</a></li>
+    <li{if $ref.isHead} class="head-ref"{/if}><a href="{$ref.url}">{$ref.name}</a></li>
   {/foreach}
   </ul>
 {/template}
diff --git a/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RepositoryIndex.soy b/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RepositoryIndex.soy
index 83b0304..13cadfd 100644
--- a/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RepositoryIndex.soy
+++ b/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RepositoryIndex.soy
@@ -22,7 +22,7 @@
  * @param cloneUrl clone URL for this repository.
  * @param description description text of the repository.
  * @param? mirroredFromUrl URL this repository is mirrored from.
- * @param branches list of branch objects with url and name keys.
+ * @param branches list of branch objects with url, name, and isHead keys.
  * @param? moreBranchesUrl URL to show more branches, if necessary.
  * @param tags list of tag objects with url and name keys.
  * @param? moreTagsUrl URL to show more branches, if necessary.