Add text support for tree paths Match the output of git ls-tree, base64-encoded. Change-Id: I5a949e69920dbc942f3f12e690213dfc3bdeaf9a
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java index bcbb1a5..1ba2477 100644 --- a/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java +++ b/gitiles-servlet/src/main/java/com/google/gitiles/PathServlet.java
@@ -50,6 +50,7 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.RawParseUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -173,19 +174,18 @@ return; } + // Write base64 as plain text without modifying any other headers, under + // the assumption that any hint we can give to a browser that this is + // base64 data might cause it to try to decode it and render as HTML, + // which would be bad. switch (wr.type) { case SYMLINK: case REGULAR_FILE: case EXECUTABLE_FILE: - // Write base64 as plain text without modifying any other headers, - // under the assumption that any hint we can give to a browser that - // this is base64 data might cause it to try to decode it and render - // as HTML, which would be bad. - res.setHeader(MODE_HEADER, String.format("%06o", wr.type.mode.getBits())); - try (Writer writer = startRenderText(req, res, null); - OutputStream out = BaseEncoding.base64().encodingStream(writer)) { - rw.getObjectReader().open(wr.id).copyTo(out); - } + writeBlobText(req, res, wr); + break; + case TREE: + writeTreeText(req, res, wr); break; default: renderTextError(req, res, SC_NOT_FOUND, "Not a file"); @@ -201,6 +201,40 @@ } } + private void setModeHeader(HttpServletResponse res, FileType type) { + res.setHeader(MODE_HEADER, String.format("%06o", type.mode.getBits())); + } + + private void writeBlobText(HttpServletRequest req, HttpServletResponse res, WalkResult wr) + throws IOException { + setModeHeader(res, wr.type); + try (Writer writer = startRenderText(req, res, null); + OutputStream out = BaseEncoding.base64().encodingStream(writer)) { + wr.getObjectReader().open(wr.id).copyTo(out); + } + } + + private void writeTreeText(HttpServletRequest req, HttpServletResponse res, WalkResult wr) + throws IOException { + setModeHeader(res, wr.type); + + try (Writer writer = startRenderText(req, res, null); + OutputStream out = BaseEncoding.base64().encodingStream(writer)) { + // Match git ls-tree format. + while (wr.tw.next()) { + FileMode mode = wr.tw.getFileMode(0); + out.write(Constants.encode(String.format("%06o", mode.getBits()))); + out.write(' '); + out.write(Constants.encode(Constants.typeString(mode.getObjectType()))); + out.write(' '); + wr.tw.getObjectId(0).copyTo(out); + out.write('\t'); + out.write(Constants.encode(QuotedString.GIT_PATH.quote(wr.tw.getNameString()))); + out.write('\n'); + } + } + } + private static RevTree getRoot(GitilesView view, RevWalk rw) throws IOException { RevObject obj = rw.peel(rw.parseAny(view.getRevision().getId())); switch (obj.getType()) {
diff --git a/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java b/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java index a6d29b6..02d7463 100644 --- a/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java +++ b/gitiles-servlet/src/test/java/com/google/gitiles/PathServletTest.java
@@ -33,6 +33,7 @@ import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.revwalk.RevBlob; +import org.eclipse.jgit.revwalk.RevTree; import org.junit.Before; import org.junit.Test; @@ -161,7 +162,7 @@ public void blobText() throws Exception { repo.branch("master").commit().add("foo", "contents").create(); String text = buildText("/repo/+/master/foo?format=TEXT", "100644"); - assertEquals("contents", new String(BaseEncoding.base64().decode(text), UTF_8)); + assertEquals("contents", decodeBase64(text)); } @Test @@ -176,7 +177,30 @@ } }).create(); String text = buildText("/repo/+/master/baz?format=TEXT", "120000"); - assertEquals("foo", new String(BaseEncoding.base64().decode(text), UTF_8)); + assertEquals("foo", decodeBase64(text)); + } + + @Test + public void treeText() throws Exception { + RevBlob blob = repo.blob("contents"); + RevTree tree = repo.tree(repo.file("foo/bar", blob)); + repo.branch("master").commit().setTopLevelTree(tree).create(); + + String expected = "040000 tree " + repo.get(tree, "foo").name() + "\tfoo\n"; + assertEquals(expected, decodeBase64(buildText("/repo/+/master/?format=TEXT", "040000"))); + + expected = "100644 blob " + blob.name() + "\tbar\n"; + assertEquals(expected, decodeBase64(buildText("/repo/+/master/foo?format=TEXT", "040000"))); + assertEquals(expected, decodeBase64(buildText("/repo/+/master/foo/?format=TEXT", "040000"))); + } + + @Test + public void treeTextEscaped() throws Exception { + RevBlob blob = repo.blob("contents"); + repo.branch("master").commit().add("foo\nbar\rbaz", blob).create(); + + assertEquals("100644 blob " + blob.name() + "\t\"foo\\nbar\\rbaz\"\n", + decodeBase64(buildText("/repo/+/master/?format=TEXT", "040000"))); } @Test @@ -197,9 +221,6 @@ }).create(); assertNotFound("/repo/+/master/nonexistent?format=TEXT"); - assertNotFound("/repo/+/master/?format=TEXT"); - assertNotFound("/repo/+/master/foo?format=TEXT"); - assertNotFound("/repo/+/master/foo/?format=TEXT"); assertNotFound("/repo/+/master/gitiles?format=TEXT"); } @@ -235,4 +256,8 @@ // the Soy data for introspection. return BaseServlet.getData(service(pathAndQuery).getRequest()); } + + private static String decodeBase64(String in) { + return new String(BaseEncoding.base64().decode(in), UTF_8); + } }