Merge "Redirect to normalized versions of parent revision expressions"
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 fc553c7..d3bf7cf 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/GitilesView.java
@@ -196,7 +196,8 @@
case LOG:
break;
default:
- checkState(revision == null, "cannot set old revision on %s view", type);
+ revision = Objects.firstNonNull(revision, Revision.NULL);
+ checkState(revision == Revision.NULL, "cannot set old revision on %s view", type);
break;
}
this.oldRevision = revision;
@@ -212,7 +213,7 @@
}
public Revision getOldRevision() {
- return revision;
+ return oldRevision;
}
public Builder setPathPart(String path) {
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/Revision.java b/gitiles-servlet/src/main/java/com/google/gitiles/Revision.java
index 3fdf777..a93d4b5 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/Revision.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/Revision.java
@@ -43,6 +43,14 @@
/** Common default branch given to clients. */
public static final Revision HEAD = named("HEAD");
+ public static Revision normalizeParentExpressions(Revision rev) {
+ if (rev == null
+ || (rev.name.indexOf("~") < 0 && rev.name.indexOf("^") < 0)) {
+ return rev;
+ }
+ return new Revision(rev.id.name(), rev.id, rev.type, rev.peeledId, rev.peeledType);
+ }
+
private final String name;
private final ObjectId id;
private final int type;
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 13537dc..e8b1055 100644
--- a/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
+++ b/gitiles-servlet/src/main/java/com/google/gitiles/ViewFilter.java
@@ -106,11 +106,16 @@
res.setStatus(SC_NOT_FOUND);
return;
}
+
@SuppressWarnings("unchecked")
Map<String, String[]> params = req.getParameterMap();
view.setHostName(urls.getHostName(req))
.setServletPath(req.getContextPath() + req.getServletPath())
.putAllParams(params);
+ if (normalize(view, res)) {
+ return;
+ }
+
setView(req, view.build());
try {
chain.doFilter(req, res);
@@ -119,6 +124,20 @@
}
}
+ private boolean normalize(GitilesView.Builder view, HttpServletResponse res)
+ throws IOException {
+ if (view.getOldRevision() != Revision.NULL) {
+ return false;
+ }
+ Revision r = view.getRevision();
+ Revision nr = Revision.normalizeParentExpressions(r);
+ if (r != nr) {
+ res.sendRedirect(view.setRevision(nr).toUrl());
+ return true;
+ }
+ return false;
+ }
+
private GitilesView.Builder parse(HttpServletRequest req) throws IOException {
String repoName = trimLeadingSlash(getRegexGroup(req, 1));
if (repoName.isEmpty()) {
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java
index 85da54f..558eb4c 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/FakeHttpServletResponse.java
@@ -208,8 +208,9 @@
}
@Override
- public synchronized void sendRedirect(String msg) {
+ public synchronized void sendRedirect(String loc) {
status = SC_FOUND;
+ setHeader(HttpHeaders.LOCATION, loc);
committed = true;
}
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 4e80c39..2660541 100644
--- a/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
+++ b/gitiles-servlet/src/test/java/com/google/gitiles/ViewFilterTest.java
@@ -20,6 +20,7 @@
import static com.google.gitiles.GitilesFilter.ROOT_REGEX;
import com.google.common.collect.ImmutableList;
+import com.google.common.net.HttpHeaders;
import com.google.common.util.concurrent.Atomics;
import com.google.gitiles.GitilesView.Type;
@@ -440,8 +441,43 @@
assertEquals("foo/bar", view.getPathPart());
}
+ public void testNormalizeParents() throws Exception {
+ RevCommit parent = repo.commit().create();
+ RevCommit master = repo.branch("refs/heads/master").commit().parent(parent).create();
+ GitilesView view;
+
+ assertEquals("/b/repo/+/master", getView("/repo/+/master").toUrl());
+ assertEquals("/b/repo/+/" + master.name(), getView("/repo/+/" + master.name()).toUrl());
+ assertEquals("/b/repo/+/" + parent.name(), getRedirectUrl("/repo/+/master~"));
+ assertEquals("/b/repo/+/" + parent.name(), getRedirectUrl("/repo/+/master^"));
+
+ view = getView("/repo/+log/master~..master/");
+ assertEquals("master", view.getRevision().getName());
+ assertEquals("master~", view.getOldRevision().getName());
+
+ view = getView("/repo/+log/master^!/");
+ assertEquals("master", view.getRevision().getName());
+ assertEquals("master^", view.getOldRevision().getName());
+ }
+
+ private String getRedirectUrl(String pathAndQuery) throws ServletException, IOException {
+ FakeHttpServletResponse res = new FakeHttpServletResponse();
+ service(pathAndQuery, Atomics.<GitilesView> newReference(), res);
+ assertEquals(302, res.getStatus());
+ return res.getHeader(HttpHeaders.LOCATION);
+ }
+
private GitilesView getView(String pathAndQuery) throws ServletException, IOException {
- final AtomicReference<GitilesView> view = Atomics.newReference();
+ AtomicReference<GitilesView> view = Atomics.newReference();
+ FakeHttpServletResponse res = new FakeHttpServletResponse();
+ service(pathAndQuery, view, res);
+ assertTrue("expected non-redirect status, got " + res.getStatus(),
+ res.getStatus() < 300 || res.getStatus() >= 400);
+ return view.get();
+ }
+
+ private void service(String pathAndQuery, final AtomicReference<GitilesView> view,
+ FakeHttpServletResponse res) throws ServletException, IOException {
HttpServlet testServlet = new HttpServlet() {
private static final long serialVersionUID = 1L;
@Override
@@ -470,9 +506,7 @@
} else {
req.setPathInfo(pathAndQuery);
}
- dummyServlet(mf).service(req, new FakeHttpServletResponse());
-
- return view.get();
+ dummyServlet(mf).service(req, res);
}
private MetaServlet dummyServlet(MetaFilter mf) {