Add view parsing for a new /+blame command Change-Id: Ib0146ed024e2a0d2af23bd45d8f0044070ce5560
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java new file mode 100644 index 0000000..53f2213 --- /dev/null +++ b/gitiles-servlet/src/main/java/com/google/gitiles/BlameServlet.java
@@ -0,0 +1,26 @@ +// Copyright (C) 2014 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 org.eclipse.jgit.lib.Config; + +/** Serves an HTML page with blame data for a commit. */ +public class BlameServlet extends BaseServlet { + private static final long serialVersionUID = 1L; + + public BlameServlet(Config cfg, Renderer renderer) { + super(cfg, renderer); + } +}
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 c6a0396..dcde5f5 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesFilter.java
@@ -244,6 +244,8 @@ return new DescribeServlet(config); case ARCHIVE: return new ArchiveServlet(config); + case BLAME: + return new BlameServlet(config, renderer); default: throw new IllegalArgumentException("Invalid view type: " + view); }
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 50923bb..fc553c7 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java
@@ -62,7 +62,8 @@ DIFF, LOG, DESCRIBE, - ARCHIVE; + ARCHIVE, + BLAME; } /** Exception thrown when building a view that is invalid. */ @@ -102,6 +103,7 @@ // Fallthrough. case PATH: case ARCHIVE: + case BLAME: path = other.path; // Fallthrough. case REVISION: @@ -219,6 +221,7 @@ case DIFF: checkState(path != null, "cannot set null path on %s view", type); break; + case BLAME: case ARCHIVE: case DESCRIBE: case REFS: @@ -313,6 +316,9 @@ case ARCHIVE: checkArchive(); break; + case BLAME: + checkBlame(); + break; } return new GitilesView(type, hostName, servletPath, repositoryName, revision, oldRevision, path, extension, params, anchor); @@ -367,6 +373,10 @@ private void checkArchive() { checkRevision(); } + + private void checkBlame() { + checkPath(); + } } public static Builder hostIndex() { @@ -405,6 +415,10 @@ return new Builder(Type.ARCHIVE); } + public static Builder blame() { + return new Builder(Type.BLAME); + } + static String maybeTrimLeadingAndTrailingSlash(String str) { if (str.startsWith("/")) { str = str.substring(1); @@ -580,6 +594,10 @@ } } break; + case BLAME: + url.append(repositoryName).append("/+blame/").append(revision.getName()).append('/') + .append(path); + break; default: throw new IllegalStateException("Unknown view type: " + type); } @@ -651,7 +669,7 @@ if (path != null) { if (type != Type.LOG && type != Type.REFS) { // The "." breadcrumb would be no different for LOG or REFS. - breadcrumbs.add(breadcrumb(".", copyWithPath().setPathPart(""))); + breadcrumbs.add(breadcrumb(".", copyWithPath(false).setPathPart(""))); } StringBuilder cur = new StringBuilder(); List<String> parts = ImmutableList.copyOf(Paths.SPLITTER.omitEmptyStrings().split(path)); @@ -663,7 +681,8 @@ String part = parts.get(i); cur.append(part).append('/'); String curPath = cur.toString(); - Builder builder = copyWithPath().setPathPart(curPath); + boolean isLeaf = i == parts.size() - 1; + Builder builder = copyWithPath(isLeaf).setPathPart(curPath); if (hasSingleTree != null && i < parts.size() - 1 && hasSingleTree.get(i)) { builder.replaceParam(PathServlet.AUTODIVE_PARAM, PathServlet.NO_AUTODIVE_VALUE); } @@ -677,7 +696,7 @@ return ImmutableMap.of("text", text, "url", url.toUrl()); } - private Builder copyWithPath() { + private Builder copyWithPath(boolean isLeaf) { Builder copy; switch (type) { case DIFF: @@ -686,6 +705,9 @@ case LOG: copy = log(); break; + case BLAME: + copy = isLeaf ? blame() : path(); + break; default: copy = path(); break;
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 50a4584..aece87b 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
@@ -49,6 +49,7 @@ private static final String CMD_ARCHIVE = "+archive"; private static final String CMD_AUTO = "+"; + private static final String CMD_BLAME = "+blame"; private static final String CMD_DESCRIBE = "+describe"; private static final String CMD_DIFF = "+diff"; private static final String CMD_LOG = "+log"; @@ -137,6 +138,8 @@ return parseArchiveCommand(req, repoName, path); } else if (command.equals(CMD_AUTO)) { return parseAutoCommand(req, repoName, path); + } else if (command.equals(CMD_BLAME)) { + return parseBlameCommand(req, repoName, path); } else if (command.equals(CMD_DESCRIBE)) { return parseDescribeCommand(repoName, path); } else if (command.equals(CMD_DIFF)) { @@ -198,6 +201,21 @@ } } + private GitilesView.Builder parseBlameCommand( + HttpServletRequest req, String repoName, String path) throws IOException { + if (path.isEmpty()) { + return null; + } + RevisionParser.Result result = parseRevision(req, path); + if (result == null || result.getOldRevision() != null || result.getPath().isEmpty()) { + return null; + } + return GitilesView.blame() + .setRepositoryName(repoName) + .setRevision(result.getRevision()) + .setPathPart(result.getPath()); + } + private GitilesView.Builder parseDescribeCommand(String repoName, String path) { if (isEmptyOrSlash(path)) { return null;
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 44e69c3..2e1e005 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/GitilesViewTest.java
@@ -559,6 +559,36 @@ assertEquals("/b/foo/bar/+archive/master/path/to/a/dir.tar.bz2", view.toUrl()); } + public void testBlame() throws Exception { + ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); + GitilesView view = GitilesView.blame() + .copyFrom(HOST) + .setRepositoryName("foo/bar") + .setRevision(Revision.unpeeled("master", id)) + .setPathPart("/dir/file") + .build(); + + assertEquals("/b", view.getServletPath()); + assertEquals(Type.BLAME, view.getType()); + assertEquals("host", view.getHostName()); + assertEquals("foo/bar", view.getRepositoryName()); + assertEquals(id, view.getRevision().getId()); + assertEquals("master", view.getRevision().getName()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("dir/file", view.getPathPart()); + assertTrue(HOST.getParameters().isEmpty()); + assertEquals("/b/foo/bar/+blame/master/dir/file", view.toUrl()); + assertEquals( + ImmutableList.of( + breadcrumb("host", "/b/?format=HTML"), + breadcrumb("foo/bar", "/b/foo/bar/"), + breadcrumb("master", "/b/foo/bar/+/master"), + breadcrumb(".", "/b/foo/bar/+/master/"), + breadcrumb("dir", "/b/foo/bar/+/master/dir"), + breadcrumb("file", "/b/foo/bar/+blame/master/dir/file")), + view.getBreadcrumbs()); + } + public void testEscaping() throws Exception { ObjectId id = ObjectId.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234"); ObjectId parent = ObjectId.fromString("efab5678efab5678efab5678efab5678efab5678");
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 4fddf0f..47013d6 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
@@ -422,6 +422,25 @@ assertEquals("foo/bar", view.getPathPart()); } + public void testBlame() throws Exception { + RevCommit master = repo.branch("refs/heads/master").commit().create(); + repo.branch("refs/heads/branch").commit().create(); + GitilesView view; + + assertNull(getView("/repo/+blame")); + assertNull(getView("/repo/+blame/")); + assertNull(getView("/repo/+blame/master")); + assertNull(getView("/repo/+blame/master..branch")); + + view = getView("/repo/+blame/master/foo/bar"); + assertEquals(Type.BLAME, view.getType()); + assertEquals("repo", view.getRepositoryName()); + assertEquals("master", view.getRevision().getName()); + assertEquals(master, view.getRevision().getId()); + assertEquals(Revision.NULL, view.getOldRevision()); + assertEquals("foo/bar", view.getPathPart()); + } + private GitilesView getView(String pathAndQuery) throws ServletException, IOException { final AtomicReference<GitilesView> view = Atomics.newReference(); HttpServlet testServlet = new HttpServlet() {