blob: 02d81afc918db6151562ff577de25e025f020a5b [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;
Dave Borowitzc8a15682013-07-02 14:33:08 -070026import org.eclipse.jgit.lib.Config;
Dave Borowitz9de65952012-08-13 16:09:45 -070027import org.eclipse.jgit.lib.ObjectId;
28import org.eclipse.jgit.lib.Repository;
29import org.eclipse.jgit.revwalk.RevCommit;
30import org.eclipse.jgit.revwalk.RevWalk;
31import org.eclipse.jgit.treewalk.AbstractTreeIterator;
32import org.eclipse.jgit.treewalk.CanonicalTreeParser;
33import org.eclipse.jgit.treewalk.EmptyTreeIterator;
34import org.eclipse.jgit.treewalk.filter.PathFilter;
Dave Borowitza03760a2014-01-29 16:17:28 -080035import org.eclipse.jgit.util.GitDateFormatter;
36import org.eclipse.jgit.util.GitDateFormatter.Format;
Dave Borowitz9de65952012-08-13 16:09:45 -070037
38import java.io.IOException;
39import java.io.OutputStream;
40import java.util.Arrays;
41import java.util.Map;
42
43import javax.servlet.http.HttpServletRequest;
44import javax.servlet.http.HttpServletResponse;
45
46/** Serves an HTML page with all the diffs for a commit. */
47public class DiffServlet extends BaseServlet {
Chad Horohoead23f142012-11-12 09:45:39 -080048 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070049 private static final String PLACEHOLDER = "id=\"DIFF_OUTPUT_BLOCK\"";
50
51 private final Linkifier linkifier;
52
Dave Borowitzc8a15682013-07-02 14:33:08 -070053 public DiffServlet(Config cfg, Renderer renderer, Linkifier linkifier) {
Dave Borowitz54271462013-11-11 11:43:11 -080054 super(cfg, renderer);
Dave Borowitz9de65952012-08-13 16:09:45 -070055 this.linkifier = checkNotNull(linkifier, "linkifier");
56 }
57
58 @Override
59 protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
60 GitilesView view = ViewFilter.getView(req);
61 Repository repo = ServletUtils.getRepository(req);
62
63 RevWalk walk = new RevWalk(repo);
64 try {
65 boolean showCommit;
66 AbstractTreeIterator oldTree;
67 AbstractTreeIterator newTree;
68 try {
69 // If we are viewing the diff between a commit and one of its parents,
70 // include the commit detail in the rendered page.
71 showCommit = isParentOf(walk, view.getOldRevision(), view.getRevision());
72 oldTree = getTreeIterator(walk, view.getOldRevision().getId());
73 newTree = getTreeIterator(walk, view.getRevision().getId());
74 } catch (MissingObjectException e) {
75 res.setStatus(SC_NOT_FOUND);
76 return;
77 } catch (IncorrectObjectTypeException e) {
78 res.setStatus(SC_NOT_FOUND);
79 return;
80 }
81
82 Map<String, Object> data = getData(req);
83 data.put("title", "Diff - " + view.getRevisionRange());
84 if (showCommit) {
Dave Borowitza03760a2014-01-29 16:17:28 -080085 GitDateFormatter df = new GitDateFormatter(Format.DEFAULT);
Dave Borowitz3b086a72013-07-02 15:03:03 -070086 data.put("commit", new CommitSoyData()
87 .setLinkifier(linkifier)
Dave Borowitzc8a15682013-07-02 14:33:08 -070088 .setArchiveFormat(archiveFormat)
Dave Borowitza03760a2014-01-29 16:17:28 -080089 .toSoyData(req, walk.parseCommit(view.getRevision().getId()), df));
Dave Borowitz9de65952012-08-13 16:09:45 -070090 }
91 if (!data.containsKey("repositoryName") && (view.getRepositoryName() != null)) {
92 data.put("repositoryName", view.getRepositoryName());
93 }
94 if (!data.containsKey("breadcrumbs")) {
95 data.put("breadcrumbs", view.getBreadcrumbs());
96 }
97
98 String[] html = renderAndSplit(data);
99 res.setStatus(HttpServletResponse.SC_OK);
100 res.setContentType(FormatType.HTML.getMimeType());
101 res.setCharacterEncoding(Charsets.UTF_8.name());
Dave Borowitz33d4fda2013-10-22 16:40:20 -0700102 setCacheHeaders(res);
Dave Borowitz9de65952012-08-13 16:09:45 -0700103
104 OutputStream out = res.getOutputStream();
105 try {
106 out.write(html[0].getBytes(Charsets.UTF_8));
Dave Borowitz33d4fda2013-10-22 16:40:20 -0700107 formatHtmlDiff(out, repo, oldTree, newTree, view.getPathPart());
Dave Borowitz9de65952012-08-13 16:09:45 -0700108 out.write(html[1].getBytes(Charsets.UTF_8));
109 } finally {
110 out.close();
111 }
112 } finally {
113 walk.release();
114 }
115 }
116
117 private static boolean isParentOf(RevWalk walk, Revision oldRevision, Revision newRevision)
118 throws MissingObjectException, IncorrectObjectTypeException, IOException {
119 RevCommit newCommit = walk.parseCommit(newRevision.getId());
120 if (newCommit.getParentCount() > 0) {
121 return Arrays.asList(newCommit.getParents()).contains(oldRevision.getId());
122 } else {
123 return oldRevision == Revision.NULL;
124 }
125 }
126
127 private String[] renderAndSplit(Map<String, Object> data) {
128 String html = renderer.newRenderer("gitiles.diffDetail")
129 .setData(data)
130 .render();
131 int id = html.indexOf(PLACEHOLDER);
132 if (id < 0) {
133 throw new IllegalStateException("Template must contain " + PLACEHOLDER);
134 }
135
136 int lt = html.lastIndexOf('<', id);
137 int gt = html.indexOf('>', id + PLACEHOLDER.length());
138 return new String[] {html.substring(0, lt), html.substring(gt + 1)};
139 }
140
141 private void formatHtmlDiff(OutputStream out,
Dave Borowitz33d4fda2013-10-22 16:40:20 -0700142 Repository repo, AbstractTreeIterator oldTree,
143 AbstractTreeIterator newTree, String path)
Dave Borowitz9de65952012-08-13 16:09:45 -0700144 throws IOException {
145 DiffFormatter diff = new HtmlDiffFormatter(renderer, out);
146 try {
147 if (!path.equals("")) {
148 diff.setPathFilter(PathFilter.create(path));
149 }
150 diff.setRepository(repo);
151 diff.setDetectRenames(true);
152 diff.format(oldTree, newTree);
153 } finally {
154 diff.release();
155 }
156 }
157
158 private static AbstractTreeIterator getTreeIterator(RevWalk walk, ObjectId id)
159 throws IOException {
160 if (!id.equals(ObjectId.zeroId())) {
161 CanonicalTreeParser p = new CanonicalTreeParser();
162 p.reset(walk.getObjectReader(), walk.parseTree(id));
163 return p;
164 } else {
165 return new EmptyTreeIterator();
166 }
167 }
168}