blob: bd97f6f0b840cbe5ef69608bdf4d5522361c20bd [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
18import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
19
20import com.google.common.base.Charsets;
21
22import org.eclipse.jgit.diff.DiffFormatter;
23import org.eclipse.jgit.errors.IncorrectObjectTypeException;
24import org.eclipse.jgit.errors.MissingObjectException;
25import org.eclipse.jgit.http.server.ServletUtils;
26import org.eclipse.jgit.lib.ObjectId;
27import org.eclipse.jgit.lib.Repository;
28import org.eclipse.jgit.revwalk.RevCommit;
29import org.eclipse.jgit.revwalk.RevWalk;
30import org.eclipse.jgit.treewalk.AbstractTreeIterator;
31import org.eclipse.jgit.treewalk.CanonicalTreeParser;
32import org.eclipse.jgit.treewalk.EmptyTreeIterator;
33import org.eclipse.jgit.treewalk.filter.PathFilter;
34
35import java.io.IOException;
36import java.io.OutputStream;
37import java.util.Arrays;
38import java.util.Map;
39
40import javax.servlet.http.HttpServletRequest;
41import javax.servlet.http.HttpServletResponse;
42
43/** Serves an HTML page with all the diffs for a commit. */
44public class DiffServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080045 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070046 private static final String PLACEHOLDER = "id=\"DIFF_OUTPUT_BLOCK\"";
47
48 private final Linkifier linkifier;
49
50 public DiffServlet(Renderer renderer, Linkifier linkifier) {
51 super(renderer);
52 this.linkifier = checkNotNull(linkifier, "linkifier");
53 }
54
55 @Override
56 protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
57 GitilesView view = ViewFilter.getView(req);
58 Repository repo = ServletUtils.getRepository(req);
59
60 RevWalk walk = new RevWalk(repo);
61 try {
62 boolean showCommit;
63 AbstractTreeIterator oldTree;
64 AbstractTreeIterator newTree;
65 try {
66 // If we are viewing the diff between a commit and one of its parents,
67 // include the commit detail in the rendered page.
68 showCommit = isParentOf(walk, view.getOldRevision(), view.getRevision());
69 oldTree = getTreeIterator(walk, view.getOldRevision().getId());
70 newTree = getTreeIterator(walk, view.getRevision().getId());
71 } catch (MissingObjectException e) {
72 res.setStatus(SC_NOT_FOUND);
73 return;
74 } catch (IncorrectObjectTypeException e) {
75 res.setStatus(SC_NOT_FOUND);
76 return;
77 }
78
79 Map<String, Object> data = getData(req);
80 data.put("title", "Diff - " + view.getRevisionRange());
81 if (showCommit) {
Dave Borowitz3b086a72013-07-02 15:03:03 -070082 data.put("commit", new CommitSoyData()
83 .setLinkifier(linkifier)
84 .toSoyData(req, walk.parseCommit(view.getRevision().getId())));
Dave Borowitz9de65952012-08-13 16:09:45 -070085 }
86 if (!data.containsKey("repositoryName") && (view.getRepositoryName() != null)) {
87 data.put("repositoryName", view.getRepositoryName());
88 }
89 if (!data.containsKey("breadcrumbs")) {
90 data.put("breadcrumbs", view.getBreadcrumbs());
91 }
92
93 String[] html = renderAndSplit(data);
94 res.setStatus(HttpServletResponse.SC_OK);
95 res.setContentType(FormatType.HTML.getMimeType());
96 res.setCharacterEncoding(Charsets.UTF_8.name());
97 setCacheHeaders(req, res);
98
99 OutputStream out = res.getOutputStream();
100 try {
101 out.write(html[0].getBytes(Charsets.UTF_8));
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700102 formatHtmlDiff(out, repo, walk, oldTree, newTree, view.getPathPart());
Dave Borowitz9de65952012-08-13 16:09:45 -0700103 out.write(html[1].getBytes(Charsets.UTF_8));
104 } finally {
105 out.close();
106 }
107 } finally {
108 walk.release();
109 }
110 }
111
112 private static boolean isParentOf(RevWalk walk, Revision oldRevision, Revision newRevision)
113 throws MissingObjectException, IncorrectObjectTypeException, IOException {
114 RevCommit newCommit = walk.parseCommit(newRevision.getId());
115 if (newCommit.getParentCount() > 0) {
116 return Arrays.asList(newCommit.getParents()).contains(oldRevision.getId());
117 } else {
118 return oldRevision == Revision.NULL;
119 }
120 }
121
122 private String[] renderAndSplit(Map<String, Object> data) {
123 String html = renderer.newRenderer("gitiles.diffDetail")
124 .setData(data)
125 .render();
126 int id = html.indexOf(PLACEHOLDER);
127 if (id < 0) {
128 throw new IllegalStateException("Template must contain " + PLACEHOLDER);
129 }
130
131 int lt = html.lastIndexOf('<', id);
132 int gt = html.indexOf('>', id + PLACEHOLDER.length());
133 return new String[] {html.substring(0, lt), html.substring(gt + 1)};
134 }
135
136 private void formatHtmlDiff(OutputStream out,
137 Repository repo, RevWalk walk,
138 AbstractTreeIterator oldTree, AbstractTreeIterator newTree,
139 String path)
140 throws IOException {
141 DiffFormatter diff = new HtmlDiffFormatter(renderer, out);
142 try {
143 if (!path.equals("")) {
144 diff.setPathFilter(PathFilter.create(path));
145 }
146 diff.setRepository(repo);
147 diff.setDetectRenames(true);
148 diff.format(oldTree, newTree);
149 } finally {
150 diff.release();
151 }
152 }
153
154 private static AbstractTreeIterator getTreeIterator(RevWalk walk, ObjectId id)
155 throws IOException {
156 if (!id.equals(ObjectId.zeroId())) {
157 CanonicalTreeParser p = new CanonicalTreeParser();
158 p.reset(walk.getObjectReader(), walk.parseTree(id));
159 return p;
160 } else {
161 return new EmptyTreeIterator();
162 }
163 }
164}