Put a shortlog on the repository index page The repo index page now shows an abbreviated list of branches and tags along the left-hand side, with the main content area taken up by a shortlog of HEAD. Since the ref list is now abbreviated, provide a new /+refs page that shows the unabbreviated ref list. (This unabbreviated list is still a bit ugly but ends up looking a lot like the old repo index page.) Change-Id: Ie72690d5e9b8162f68818782bfdaa568ad319983
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java index f463445..73bc426 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java
@@ -224,6 +224,8 @@ return new HostIndexServlet(renderer, urls, accessFactory); case REPOSITORY_INDEX: return new RepositoryIndexServlet(renderer, accessFactory, timeCache); + case REFS: + return new RefServlet(renderer, timeCache); case REVISION: return new RevisionServlet(renderer, linkifier()); case PATH:
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java index e84bb2a..d5ac93b 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java
@@ -51,6 +51,7 @@ public static enum Type { HOST_INDEX, REPOSITORY_INDEX, + REFS, REVISION, PATH, DIFF, @@ -88,6 +89,7 @@ case REVISION: revision = other.revision; // Fallthrough. + case REFS: case REPOSITORY_INDEX: repositoryName = other.repositoryName; // Fallthrough. @@ -139,6 +141,7 @@ switch (type) { case HOST_INDEX: case REPOSITORY_INDEX: + case REFS: throw new IllegalStateException(String.format("cannot set revision on %s view", type)); default: this.revision = checkNotNull(revision); @@ -244,6 +247,9 @@ case REPOSITORY_INDEX: checkRepositoryIndex(); break; + case REFS: + checkRefs(); + break; case REVISION: checkRevision(); break; @@ -275,6 +281,10 @@ checkHostIndex(); } + private void checkRefs() { + checkRepositoryIndex(); + } + private void checkRevision() { checkState(revision != Revision.NULL, "missing revision on %s view", type); checkRepositoryIndex(); @@ -302,6 +312,10 @@ return new Builder(Type.REPOSITORY_INDEX); } + public static Builder refs() { + return new Builder(Type.REFS); + } + public static Builder revision() { return new Builder(Type.REVISION); } @@ -426,6 +440,9 @@ case REPOSITORY_INDEX: url.append(repositoryName).append('/'); break; + case REFS: + url.append(repositoryName).append("/+refs"); + break; case REVISION: url.append(repositoryName).append("/+/").append(revision.getName()); break;
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/LogSoyData.java b/gitiles-servlet/src/main/java/com/google/gitiles/LogSoyData.java index dbeb2e9..77bd36a 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/LogSoyData.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/LogSoyData.java
@@ -14,13 +14,9 @@ package com.google.gitiles; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.gitiles.CommitSoyData.KeySet; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectId; @@ -29,9 +25,13 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.gitiles.CommitSoyData.KeySet; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; public class LogSoyData { private final HttpServletRequest req;
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java new file mode 100644 index 0000000..82c5b46 --- /dev/null +++ b/gitiles-servlet/src/main/java/com/google/gitiles/RefServlet.java
@@ -0,0 +1,105 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gitiles; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Ordering; +import com.google.common.util.concurrent.UncheckedExecutionException; + +import org.eclipse.jgit.http.server.ServletUtils; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefComparator; +import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.revwalk.RevWalk; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** Serves an HTML page with all the refs in a repository. */ +public class RefServlet extends BaseServlet { + private static final long serialVersionUID = 1L; + + private final TimeCache timeCache; + + protected RefServlet(Renderer renderer, TimeCache timeCache) { + super(renderer); + this.timeCache = checkNotNull(timeCache, "timeCache"); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { + RevWalk walk = new RevWalk(ServletUtils.getRepository(req)); + List<Map<String, String>> tags; + try { + tags = getTags(req, timeCache, walk, 0); + } finally { + walk.release(); + } + render(req, res, "gitiles.refsDetail", ImmutableMap.of( + "branches", getBranches(req, 0), + "tags", tags)); + } + + static List<Map<String, String>> getBranches(HttpServletRequest req, int limit) + throws IOException { + return getRefs(req, Constants.R_HEADS, Ordering.from(RefComparator.INSTANCE), 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> tagComparator(final TimeCache timeCache, final RevWalk walk) { + return Ordering.natural().onResultOf(new Function<Ref, Long>() { + @Override + public Long apply(Ref ref) { + try { + return timeCache.getTime(walk, ref.getObjectId()); + } catch (IOException e) { + throw new UncheckedExecutionException(e); + } + } + }).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(); + 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()); + + 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)); + } + return result; + } +}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/Renderer.java b/gitiles-servlet/src/main/java/com/google/gitiles/Renderer.java index dc4f680..aa12869 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/Renderer.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/Renderer.java
@@ -42,6 +42,7 @@ "LogDetail.soy", "ObjectDetail.soy", "PathDetail.soy", + "RefList.soy", "RevisionDetail.soy", "RepositoryIndex.soy");
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 11f0e9e..a0fea72 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/RepositoryIndexServlet.java
@@ -17,22 +17,18 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; import com.google.common.base.Strings; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Ordering; -import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.common.collect.Maps; import org.eclipse.jgit.http.server.ServletUtils; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefComparator; -import org.eclipse.jgit.lib.RefDatabase; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import java.io.IOException; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -43,6 +39,9 @@ public class RepositoryIndexServlet extends BaseServlet { private static final long serialVersionUID = 1L; + static final int REF_LIMIT = 10; + private static final int LOG_LIMIT = 20; + private final GitilesAccess.Factory accessFactory; private final TimeCache timeCache; @@ -60,49 +59,48 @@ @VisibleForTesting Map<String, ?> buildData(HttpServletRequest req) throws IOException { + GitilesView view = ViewFilter.getView(req); + Repository repo = ServletUtils.getRepository(req); RepositoryDescription desc = accessFactory.forRequest(req).getRepositoryDescription(); - RevWalk walk = new RevWalk(ServletUtils.getRepository(req)); + RevWalk walk = new RevWalk(repo); List<Map<String, String>> tags; + Map<String, Object> data; try { - tags = getRefs(req, Constants.R_TAGS, tagComparator(walk)); + tags = RefServlet.getTags(req, timeCache, walk, REF_LIMIT); + ObjectId headId = repo.resolve(Constants.HEAD); + if (headId != null) { + RevObject head = walk.parseAny(repo.resolve(Constants.HEAD)); + if (head.getType() == Constants.OBJ_COMMIT) { + walk.reset(); + walk.markStart((RevCommit) head); + data = new LogSoyData(req, repo, view).toSoyData(walk, LOG_LIMIT, "HEAD", null); + } else { + // TODO(dborowitz): Handle non-commit or missing HEAD? + data = Maps.newHashMapWithExpectedSize(6); + } + } else { + data = Maps.newHashMapWithExpectedSize(6); + } } finally { walk.release(); } - return ImmutableMap.of("cloneUrl", desc.cloneUrl, - "mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl), - "description", Strings.nullToEmpty(desc.description), - "branches", getRefs(req, Constants.R_HEADS, Ordering.from(RefComparator.INSTANCE)), - "tags", tags); - } + List<Map<String, String>> branches = RefServlet.getBranches(req, REF_LIMIT); - private List<Map<String, String>> getRefs(HttpServletRequest req, String prefix, - Ordering<Ref> ordering) throws IOException { - RefDatabase refdb = ServletUtils.getRepository(req).getRefDatabase(); - Collection<Ref> refs = ordering.sortedCopy(refdb.getRefs(prefix).values()); - List<Map<String, String>> 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)); + data.put("cloneUrl", desc.cloneUrl); + data.put("mirroredFromUrl", Strings.nullToEmpty(desc.mirroredFromUrl)); + data.put("description", Strings.nullToEmpty(desc.description)); + data.put("branches", trim(branches)); + if (branches.size() > REF_LIMIT) { + data.put("moreBranchesUrl", GitilesView.refs().copyFrom(view).toUrl()); } - - return result; + data.put("tags", trim(tags)); + if (tags.size() > REF_LIMIT) { + data.put("moreTagsUrl", GitilesView.refs().copyFrom(view).toUrl()); + } + return data; } - private Ordering<Ref> tagComparator(final RevWalk walk) { - return Ordering.natural().onResultOf(new Function<Ref, Long>() { - @Override - public Long apply(Ref ref) { - try { - return timeCache.getTime(walk, ref.getObjectId()); - } catch (IOException e) { - throw new UncheckedExecutionException(e); - } - } - }).reverse().compound(RefComparator.INSTANCE); + private static <T> List<T> trim(List<T> list) { + return list.size() > REF_LIMIT ? list.subList(0, REF_LIMIT) : list; } }
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java index e7532ab..90cd94a 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
@@ -18,6 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull; import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; +import org.eclipse.jgit.http.server.ServletUtils; +import org.eclipse.jgit.http.server.glue.WrappedRequest; + import java.io.IOException; import java.util.Map; @@ -26,9 +29,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jgit.http.server.ServletUtils; -import org.eclipse.jgit.http.server.glue.WrappedRequest; - /** Filter to parse URLs and convert them to {@link GitilesView}s. */ public class ViewFilter extends AbstractHttpFilter { // TODO(dborowitz): Make this public in JGit (or implement getRegexGroup @@ -41,6 +41,7 @@ private static final String CMD_AUTO = "+"; private static final String CMD_DIFF = "+diff"; private static final String CMD_LOG = "+log"; + private static final String CMD_REFS = "+refs"; private static final String CMD_SHOW = "+show"; public static GitilesView getView(HttpServletRequest req) { @@ -101,10 +102,12 @@ // Non-path cases. if (repoName.isEmpty()) { return GitilesView.hostIndex(); + } else if (command.equals(CMD_REFS) && path.isEmpty()) { + return GitilesView.refs().setRepositoryName(repoName); } else if (command.isEmpty()) { return GitilesView.repositoryIndex().setRepositoryName(repoName); } else if (path.isEmpty()) { - return null; // Command but no path. + return null; // Command that requires a path, but no path. } path = trimLeadingSlash(path); @@ -128,6 +131,8 @@ } } else if (CMD_DIFF.equals(command)) { view = GitilesView.diff().setTreePath(path); + } else if (CMD_REFS.equals(command)) { + view = GitilesView.repositoryIndex(); } else { return null; // Bad command. }
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 d898767..a41b88c 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
@@ -126,15 +126,17 @@ padding-left: 1em; } .repository-refs { - width: 650px; -} -.repository-branches { float: left; - width: 300px; + width: 200px; + margin-left: -100%; } -.repository-tags { - float: right; - width: 350px; +.repository-shortlog-wrapper { + float: left; + width: 100%; +} +.repository-shortlog { + margin-top: 20px; + margin-left: 200px; } .clone-line { background-color: #e5ecf9; /* BOX_BACKGROUND */ @@ -144,6 +146,17 @@ font-size: 9pt; } +/* Styles for the ref detail page. */ + +.refs-branches { + float: left; + width: 200px; +} +.refs-tags { + float: left; + width: 200px; +} + /* 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 new file mode 100644 index 0000000..751ec86 --- /dev/null +++ b/gitiles-servlet/src/main/resources/com/google/gitiles/templates/RefList.soy
@@ -0,0 +1,68 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +{namespace gitiles autoescape="contextual"} + + +/** + * List of all refs in a repository. + * + * @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 tags list of tag objects with url and name keys. + */ +{template .refsDetail} +{call .header} + {param title: 'Refs' /} + {param repositoryName: $repositoryName /} + {param menuEntries: $menuEntries /} + {param breadcrumbs: $breadcrumbs /} +{/call} + +{if length($branches)} + <div class="refs-branches"> + {call .refList} + {param type: 'Branches' /} + {param refs: $branches /} + {/call} + </div> +{/if} + +{if length($tags)} + <div class="refs-tags"> + {call .refList} + {param type: 'Tags' /} + {param refs: $tags /} + {/call} + </div> +{/if} + +{call .footer /} +{/template} + +/** + * 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 + */ +{template .refList} + <h3>{$type}</h3> + <ul> + {foreach $ref in $refs} + <li><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 9dfe342..83b0304 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,8 +22,13 @@ * @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? tags list of tag objects with url and name keys. + * @param branches list of branch objects with url and name 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. + * @param? nextUrl URL for the next page of log results. + * @param? previousUrl URL for the previous page of log results. + * @param entries list of log entries; see .logEntry. */ {template .repositoryIndex} {call .header} @@ -52,38 +57,59 @@ git clone {$cloneUrl} </textarea> -<div class="repository-refs"> - {if $branches and length($branches)} - <div class="repository-branches"> - <h3>Branches</h3> - <ul class="branch-list"> - {foreach $branch in $branches} - {call .ref_ data="$branch" /} - {/foreach} - </ul> +{if length($entries) and (length($branches) or length($tags))} + <div class="repository-shortlog-wrapper"> + <div class="repository-shortlog"> + {call .logEntries data="all" /} </div> - {/if} + </div> - {if $tags and length($tags)} - <div class="repository-tags"> - <h3>Tags</h3> - <ul class="branch-list"> - {foreach $tag in $tags} - {call .ref_ data="$tag" /} - {/foreach} - </ul> - </div> - {/if} -</div> + <div class="repository-refs"> + {call .branches_ data="all" /} + {call .tags_ data="all" /} + </div> +{elseif length($entries)} + {call .logEntries data="all" /} +{elseif length($branches) or length($tags)} + {call .branches_ data="all" /} + {call .tags_ data="all" /} +{/if} + {call .footer /} {/template} /** - * Detail for a single ref. + * List of branches. * - * @param url URL for ref detail page. - * @param name ref name. + * @param? branches list of branch objects with url and name keys. + * @param? moreBranchesUrl URL to show more branches, if necessary. */ -{template .ref_} -<li><a href="{$url}">{$name}</a></li> +{template .branches_ private="true"} + {if length($branches)} + {call .refList} + {param type: 'Branches' /} + {param refs: $branches /} + {/call} + {if $moreBranchesUrl} + <a href="{$moreBranchesUrl}">{msg desc="link to view more branches"}More...{/msg}</a> + {/if} + {/if} +{/template} + +/** + * List of tags. + * + * @param? tags list of branch objects with url and name keys. + * @param? moreTagsUrl URL to show more tags, if necessary. + */ +{template .tags_ private="true"} + {if length($tags)} + {call .refList} + {param type: 'Tags' /} + {param refs: $tags /} + {/call} + {if $moreTagsUrl} + <a href="{$moreTagsUrl}">{msg desc="link to view more tags"}More...{/msg}</a> + {/if} + {/if} {/template}
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java index 1862467..b2b435a 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java
@@ -117,6 +117,28 @@ view.getBreadcrumbs()); } + public void testRefs() throws Exception { + GitilesView view = GitilesView.refs() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.REFS, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertNull(view.getTreePath()); + assertTrue(HOST.getParameters().isEmpty()); + + assertEquals("/b/foo/bar/+refs", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/")), + view.getBreadcrumbs()); + } + public void testRefWithRevision() throws Exception { ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); GitilesView view = GitilesView.revision()
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java index 800bd38..265c9cc 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
@@ -96,6 +96,15 @@ assertNull(view.getTreePath()); } + public void testRefs() throws Exception { + GitilesView view = getView("/repo/+refs"); + assertEquals(Type.REFS, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals(Revision.NULL, view.getRevision()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertNull(view.getTreePath()); + } + public void testBranches() throws Exception { RevCommit master = repo.branch("refs/heads/master").commit().create(); RevCommit stable = repo.branch("refs/heads/stable").commit().create();